使用Struts2或webwork2有一段时间了,想把Struts2框架的思路简单的与大家分享一下,之前我是看过Struts2源代码的,所以本文算是它的一个功能非常有限的压缩版本。我也不打算重复发明轮子,只想让Struts2或Webwork2的新手更多的了解框架本身,而不仅仅是应用。废话少说,开始吧。

本文采用基本Xml来配置Action,如果有时间会继续写Annotation的实现。Xml文件结构与Struts2的配置文件struts.xml几乎一样,这样大家都比较熟悉,不过我简写了某些地方:

Struts.xml

Xml代码  
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!-- 为简化框架,package的属性都没有实现;而且所有的元素都是简化版的 -->
  3. <struts>
  4. <package>
  5. <action name="hello" method="hello"
  6. class="com.leo.action.HelloAction">
  7. <result name="success">/index.jsp</result>
  8. </action>
  9. </package>
  10. </struts>

是不是很熟悉啊,不过为了简单我都给简化了,否则这是一个没有尽头的工作。核心部分仍然是Filter,Struts2所有工作都是通过一个Filter来完成的(struts1.*是通过一个Action实现的)。我们先来看代码:

Java代码  
  1. public class StrutsFilter implements Filter {
  2. public void doFilter(ServletRequest req, ServletResponse res,
  3. FilterChain chain) throws IOException, ServletException {
  4. // TODO Auto-generated method stub
  5. HttpServletRequest request = (HttpServletRequest) req;
  6. HttpServletResponse response = (HttpServletResponse) res;
  7. ServletContext servletContext = filterConfig.getServletContext();
  8. // 解析Request的URL和传过来的参数
  9. String actionName = StringUtil.parseServletPath(request
  10. .getServletPath());
  11. // 如果后缀不为.action,那么直接放过,不进行拦截
  12. if (StringUtil.isEmpty(actionName)) {
  13. chain.doFilter(request, response);
  14. } else {
  15. // 解析得到ActionClass,里面包括Action的类全名,返回页面值,Action执行的方法
  16. ActionClass clas = this.getActionClass(actionName);
  17. // 得到页面的所有parameters参数(没考虑上传情况)
  18. Map<String, String[]> params = request.getParameterMap();
  19. // 为要调用的Action的set**方法设值,并返回要调用的Action对象本身
  20. setBeforeActionValue(clas, params);
  21. // 调用的Action执行方法,并返回值设置在request域中
  22. setResultValue(clas, request);
  23. // 返回相应的JSP页面
  24. servletContext.getRequestDispatcher(clas.getResult()).forward(
  25. request, response);
  26. }
  27. }
  28. }

没错一些因果都因doFilter方法而起。我的作法是:

  • 解析URL路径,从而得到相应action在struts.xml配置文件中所配置的方法。如果不为合法的action后缀,直接chain.doFilter(request, response)放行。
  • 将URL上的参数通过request.getParameterMap()取出,在调用action执行方法之前,将具有set**属性的字段赋值。这里利用了反射。
  • 继续利用反射,执行Action的方法。结束前将具有get**属性的字段存于request域中,供页面使用。大家记得在每次执行Action的方法时都会返回一个String字符串,比如SUCCESS,INPUT,ERROR等,我们可以将这个值与struts.xml的<result name="success" ....>/index.jsp</result> 结点进行匹配,取出返回页面/index.jsp
  • 一切准备就绪后,调用servletContext.getRequestDispatcher().forward()方法到相应的页面上去。

这就是大概的流程。因为是一个入门的框架所以很不完善,拦截器,result type的各种类型都没有去实现,因为我压根没想过要重复发明轮子。好,我们开始一步一步的看。

我们先来看这一句:ActionClass clas = this.getActionClass(actionName);
其中ActionClass的结构如下:

Java代码  
  1. public class ActionClass {
  2. /**
  3. * 类名
  4. */
  5. private String className;
  6. /**
  7. * 要调用的方法名
  8. */
  9. private String method;
  10. /**
  11. * 返回结果页面
  12. */
  13. private String result;
  14. /**
  15. * 临时存储Action下的所有result结点
  16. */
  17. private List<Element> elements = new ArrayList<Element>();
  18. /**
  19. * 要调用的Action本身
  20. */
  21. private Object action;
  22. //省略所有的set,get方法
  23. }

ActionClass主要是用来存放解析struts.xml文件一些有用的值,以及反射时所调用的Action本身对象,其实就是一个简单的JavaBean,存储临信息。

getActionClass(actionName)方法就是将URL上的actionName取出与struts.xml中的<action>结点匹配,可以得到Action的类全名,Action所调用的具体哪个方法名,Action的所有result结点(因为方法还没有执行,还不知道是具体哪一个result结点,所以先存起来,后面来解析)分别存在ActionClass对象中相应的属性中去。具体的解析代码我就不打出来了,否则文章太长。

再来看看setBeforeActionValue(clas, params)这一句。其实就是将得到的ActionClass对象与提交的参数全部传进去,给Action的那些set属性赋值:

Java代码  
  1. /**
  2. * 调用Action,并执行Action的无参方法
  3. *
  4. * @param actionClass
  5. * @param request.getParameterMap()
  6. * @return
  7. */
  8. public Object setActionValues(ActionClass actionClass,
  9. Map<String, String[]> params) {
  10. try {
  11. // 得到Action的Class,并根据无参构造函数生成一个Action对象
  12. Class clas = Class.forName(actionClass.getClassName());
  13. Object obj = clas.newInstance();
  14. if (params != null && params.size() > 0) {
  15. Iterator<String> it = params.keySet().iterator();
  16. while (it.hasNext()) {
  17. String key = it.next();
  18. String[] value = params.get(key);
  19. String upperFirstLetter = key.substring(0, 1).toUpperCase();
  20. // 获得和属性对应的setXXX()方法的名字
  21. String setMethodName = "set" + upperFirstLetter
  22. + key.substring(1);
  23. Method method = null;
  24. // 看看该页面提交的参数名中,是否在Action有set方法
  25. try {
  26. method = clas.getMethod(setMethodName,
  27. new Class[] { String.class });
  28. } catch (NoSuchMethodException e) {
  29. System.out.println("警告 " + actionClass.getClassName()
  30. + "." + setMethodName + "("
  31. + String.class.getName() + ") 不存在");
  32. }
  33. if (method != null) {
  34. // 如果有set方法,就调用set方法,进行赋值操作
  35. String result = StringUtil.StringArrayToString(value);
  36. method.invoke(obj, new String[] { result });
  37. }
  38. }
  39. }
  40. return obj;
  41. ......
  42. }

这样就顺利的将页面的值赋给了Action的相应属性,接下来就是Action调用工作了。通过setActionValues方法,我们已经可以得到Action对象本身了,可以存在ActionClass对象clas中去,我们直接调用setResultValue(clas, request)在Action执行后,同时也把有get方法的属性一并存于request域中:

Java代码  
  1. /**
  2. * 调用Action,并执行Action的无参方法
  3. *
  4. * @param actionClass
  5. * @param obj
  6. *            要处理的对象
  7. * @return
  8. */
  9. public Object invokeAction(ActionClass actionClass) {
  10. try {
  11. Object obj = actionClass.getAction();
  12. Class clas = obj.getClass();
  13. Method method = clas.getMethod(actionClass.getMethod(), null);
  14. String result = (String) method.invoke(obj, null);
  15. this.setInvokeResult(result, actionClass);
  16. actionClass.setAction(obj);
  17. return obj;
  18. ......
  19. }
  20. }

很简单的代码——调用Action那个无参执行方法,得到返回String类型的返回结果,然后我们可以再次利用ActionClass将最终的返回结果也给解析出来,看this.setInvokeResult(result, actionClass)方法

Java代码  
  1. /**
  2. * 匹配<result name="success">/index.jsp</result> Xml中的result
  3. * name属性,如果匹配成功,设置返回结果"/index.jsp"
  4. *
  5. * @param result
  6. * @param actionClass
  7. */
  8. private void setInvokeResult(String result, ActionClass actionClass) {
  9. List<Element> elements = actionClass.getElements();
  10. for (Element elem : elements) {
  11. Attribute name = XmlUtil.getAttributeByName(elem, "name");
  12. if (StringUtil.equals(result, name.getText())) {
  13. actionClass.setResult(elem.getText());
  14. return;
  15. }
  16. }
  17. throw new RuntimeException("请确定在xml配置文件中是否有名叫 [" + result
  18. + "] 的返回类型结点 ");
  19. }

一切大功告成,将刚刚得到的返回结果用servletContext.getRequestDispatcher(clas.getResult()).forward(
request, response)转发出去,编码部分完毕。

别忘记了在web.xml中配置这个Filter:

Xml代码  
  1. <filter>
  2. <filter-name>struts</filter-name>
  3. <filter-class>com.framework.core.StrutsFilter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>struts</filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </filter-mapping>

最后写个测试Action吧,就按照本文最开始的那个struts.xml配置编写HelloAction.java

Java代码  
  1. public class HelloAction {
  2. private String message;
  3. public String hello() {
  4. message = "superleo " + this.message;
  5. return "success";
  6. }
  7. public String getMessage() {
  8. return message;
  9. }
  10. public void setMessage(String message) {
  11. this.message = message;
  12. }
  13. }

自己动手写一个Struts2相关推荐

  1. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  2. 自己动手写一个印钞机 第四章

    2019独角兽企业重金招聘Python工程师标准>>> 作者:阿布? 未经本人允许禁止转载 ipython notebook git版本 目录章节地址: 自己动手写一个印钞机 第一章 ...

  3. Spring Boot 动手写一个 Start

    我们在使用SpringBoot 项目时,引入一个springboot start依赖,只需要很少的代码,或者不用任何代码就能直接使用默认配置,再也不用那些繁琐的配置了,感觉特别神奇.我们自己也动手写一 ...

  4. 自己动手写一个nodejs的日志生成器

    自己动手写一个nodejs的logger 最近正在边学边用node.js开发个人应用的server,由于有用到websocket相关,想对websocket的通信选择性的做下日志记录,所以萌发了自己动 ...

  5. 自己动手写一个印钞机 第二章

    2019独角兽企业重金招聘Python工程师标准>>> 作者:阿布? 未经本人允许禁止转载 ipython notebook git版本 目录章节地址: 自己动手写一个印钞机 第一章 ...

  6. 学习较底层编程:动手写一个C语言编译器

    动手编写一个编译器,学习一下较为底层的编程方式,是一种学习计算机到底是如何工作的非常有效方法. 编译器通常被看作是十分复杂的工程.事实上,编写一个产品级的编译器也确实是一个庞大的任务.但是写一个小巧可 ...

  7. 自己动手写一个 strace

    这次主要分享一下一个动手的东西,就是自己动手写一个 strace 工具. 用过 strace 的同学都知道,strace 是用来跟踪进程调用的 系统调用,还可以统计进程对 系统调用 的统计等.stra ...

  8. java 同步锁_死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  9. 吕文翰 php,自己动手写一个 iOS 网络请求库(三)——降低耦合

    自己动手写一个 iOS 网络请求库(三)--降低耦合 2015-5-22 / 阅读数:16112 / 分类: iOS & Swift 本文中,我们将一起降低之前代码的耦合度,并使用适配器模式实 ...

最新文章

  1. 三星 arm9 linux,基于arm9内核三星s3c2410平台下linux四键按键驱动程序
  2. 清华姚班校友陈丹琦斩获2022斯隆奖!「诺奖风向标」27位华人学者入选
  3. 圈子 | 苏宁易购产品经理:平台支撑型产品的思维方式
  4. 针对当前项目SLED存在短缺XX天
  5. 网络基础知识--子网划分
  6. JavaScript学习总结(七)——JavaScript函数(function)
  7. 国内首家,携程试点每周两天居家办公反响热烈,76%的员工主动报名
  8. python遗传算法求解TSP问题
  9. 敏感型人格的特征,怎么改变敏感型性格?
  10. 模拟退火算法(SA)
  11. 小波 奇异点 matlab,用Matlab小波变换检测奇异点.doc
  12. photoshop修色圣典 第5版pdf
  13. 戴尔电脑,耳机插入无反应解决办法
  14. c++(标准模板库STL)
  15. 2023情人节送另一半什么好?四款适合送女友的数码好物分享
  16. 华为手机刷微博体验更好?技术角度的分析和思考,Android基础72问
  17. 如何在Django后台添加pagedown
  18. 为高效学习神器 Anki 部署一个专属同步服务器
  19. 计算机键盘键盘一个按键坏了,我的世界:如果键盘坏掉一个键,六种情况,哪个会影响你玩MC?...
  20. Vue处理后台返回List集合带有Object对象的数据

热门文章

  1. 【Flutter】Flutter 启动白屏问题 ( 问题描述 | 在 launch_background.xml 中设置启动过渡 UI )
  2. 【组合数学】排列组合 ( 多重集组合数示例 | 三个计数模型 | 选取问题 | 多重集组合问题 | 不定方程非负整数解问题 )
  3. 《Linux 性能及调优指南》1.6 了解Linux性能指标
  4. 提升vector性能的几个技巧
  5. 小程序---canvas画图,生成分享图片,画图文字换行
  6. C#文件夹权限操作工具类
  7. ESPCMS基本导航操作
  8. 关于批量发布blog的问题
  9. Vue组件实现函数防抖
  10. 开发一个自己的 CSS 框架(二)