Struts2-04-struts2工作原理
2016-02-08 17:40
531 查看
1. 简介
首先呢,网上讲述struts2的早期版本,从FilterDispatcher(现在已过时)讲起的太多了,然后我将针对比较新的版本的struts2原理进行讲解。核心过滤器是:<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
然而虽然核心过滤器改变了,但是struts2的工作原理基本和以往的基本一致,官方架构图如下:
2. StrutsPrepareAndExecuteFilter 简介
首先看一下类的介绍:意思呢就是这个过滤器的处理分为准备(preparation)和执行(execution)两个过程,看过滤器名字就能知道。那么这个过滤器的准备阶段肯定是初始化配置在init方法中执行,执行阶段肯定就是在doFilter方法之中了。
struts2的大致执行流程:
1. 启动服务器的时候对过滤器进行初始化,struts2大致完成PrepareOperations对象和ExecuteOperations对象的初始化,还有一个比较重要的对象Dispatcher对象的初始化(基本是初始化默认配置以及用户的struts.xml配置)。
2. 当拦截到一个请求之后,首先创建一个ActionContext对象,并初始化request,session,parameters,application,attr和值栈,将这些对象存放到ActionContext中,然后将ActionContext的这个实例存放到ThreadLocal中。这里到时候在具体分析,这里就是对ActionContext进行初始化。
3. 通过对请求url的解析获取ActionMapping对象,并判断这次请求是否需要让struts2进行处理。
4. 如果由struts2进行处理的话,就创建ActionProxy对象执行execute()方法。
5. 然后根据配置(ConfigManager)创建ActionInvocation的实例,调用invoke()方法。方法中就是对拦截器链的递归调用以及对最后Action的执行,当执行完毕返回一个结果串。
6. 最后根据这个返回串确定是转发,重定向还是到另一个Action等的操作,将jsp或者FreeMarker模板响应给浏览器。
3. 核心过滤器解析
下面就对上面的执行过程一个个来分析。可能会不是很详细,不过自己可以跟踪代码去看看。。若是有什么错误还望指出。3.1 成员变量
protected PrepareOperations prepare; protected ExecuteOperations execute; protected List<Pattern> excludedPatterns = null;
第一个和第二个顾名思义是整个过滤器比较重要的两个对象(准备和执行)。第三个参数表示不包含的匹配表达式,实际是用来过滤掉不包含的url的。这里先说一下不太重要的 excludedPatterns:
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); /** 跟踪代码进去,其实是将常量"struts.action.excludePattern"配置的表达式封装成一个List. 这里比如可以在struts.xml文件中配置不包含css,js等url的处理. 如配置: <constant name="struts.action.excludePattern" value="/res/.*,/css/.*,/images/.*,/js/.*,/services/.*" /> 参考:http://xingguangsixian.iteye.com/blog/2088617 */
初始化这个成员变量之后,那么在doFilter()处理如下:
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { ...... ...... /** 在这里就是对请求url和excludedPatterns的判断,如果包括的正则包含url,那么就不处理,过滤器直接放行. */
3.2 init()方法
public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); Dispatcher dispatcher = null; try { FilterHostConfig config = new FilterHostConfig(filterConfig); init.initLogging(config); dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config, dispatcher); prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); postInit(dispatcher, filterConfig); } finally { if (dispatcher != null) { dispatcher.cleanUpAfterInit(); } init.cleanup(); } }
1. 首先是对config进行一下包装,内部没什么特别的。然后initLogging,看函数名也知道对日志的初始化,内部也比较简单,如果config指定了loggerFactory的类,那么就用用户自定义的,否则使用系统默认的。
2. 初始化dispatcher,这句话过滤器执行时间比较长的一部分。不过跟踪进去大致可以知道是对各种配置文件进行的初始化,包括用户定义的struts.xml文件。
3. 然后是对prepare和execute进行初始化,其实这两个类的主要操作也是依赖dispatcher对象的。
4. 初始化excludedPatterns对象,这里不多说了。
5. postInit()方法,是个protected函数(是个空函数),可以继承进行覆盖,作为用户自定义初始化的一个回调。
6. 最后finally清理下struts的ActionContext等对象。
3.3 doFilter()函数
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { prepare.setEncodingAndLocale(request, response); prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); request = prepare.wrapRequest(request); ActionMapping mapping = prepare.findActionMapping(request, response, true); if (mapping == null) { boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { execute.executeAction(request, response, mapping); } } } finally { prepare.cleanupRequest(request); } }
1. 将request和response强转下,没啥多说。
**2. **if判断的内容上面说过,不在累赘。主要看else的部分,是对struts2处理请求和响应的部分。
3. 设置编码(如果是get请求获取参数,自行编写过滤器。网上的教程一般都有讲解),然后是创建ActionContext上下文。创建上下文主要是针对当前的请求,将request,response,application,值栈ValueStack,parameters,attr等对象放置到ActionContext中,实际是ognl上下文。
4. 将当前的prepare对象通过ThreadLocal存放到当前线程中。然后是对request进行包装,这里的包装主要正对普通参数和文件上传两种request。
MultiPartRequestWrapper是StrutsRequestWrapper的子类,比较重要的就是StrutsRequestWrapper对getAttribute()方法的覆盖,这里在上篇博文已经讲述过,这就是为什么在jsp中使用EL表达式也能获取到ActionContext中变量值的原因。
5. 然后通过解析url获取ActionMapping对象,解析出对应struts.xml文件的Action的各个属性(name,namespace,method等等)。然后判断如果存在映射就使用execute对象执行Action,否则放行。
6. 最后清理一下整个请求的环境。
3.4 执行Action
实际调用dispatcher的serviceAction()方法。如果按照正常的请求流程,内部会创建一个ActionProxy代理对象,执行execute()方法。在这个方法中调用ActionInvocation对象的实例执行invoke()方法。自己内部跟踪看看就知道了。然后是invoke()方法:
if (interceptors.hasNext()) { final InterceptorMapping interceptor = interceptors.next(); String interceptorMsg = "interceptor: " + interceptor.getName(); UtilTimerStack.push(interceptorMsg); try { resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); } finally { UtilTimerStack.pop(interceptorMsg); } } else { resultCode = invokeActionOnly(); }
这段代码可以看出是对拦截器链的递归执行,这也就是为什么我们自定义拦截器的时候,不返回字符串就要编写invocation.invoke()的原因。
如果拦截器执行完毕,那么就执行Action,内部细节就不多说了。
这个方法中,最后获取到返回的result之后,就对result进行处理,这里面就包含很多了,如转发,重定向,另一个Action啊等等。
4. 总结
其实StrutsPrepareAndExecuteFilter的工作流程也不是很难,关键就是在一些细节方面的处理。这里就简单聊一聊它的简单原理,其实也不是特别的难。结合代码在看看框架图,整个struts2的工作结果基本就略知一二了。每天进步一点点,大家加油。相关文章推荐
- DNS的工作原理图解说明
- Node.js中require的工作原理浅析
- jQuery源码分析-04 选择器-Sizzle-工作原理分析
- javaScript中的this示例学习详解及工作原理
- Web程序工作原理详解
- 深度剖析Java中的内存原型及工作原理
- PHP CodeIgniter框架的工作原理研究
- JavaScript mapreduce工作原理简析
- 交换机的工作原理
- WinSniffer
- 以太网相关知识
- 防火墙简介
- 2012-08-21 09:53 《SQL调优》课件下载共享,非常火热的课程
- C#实现的一个内存Ini类
- nginx与php,mysql的安装,配置与优化
- 在多层交换中实现网络的冗余讲解
- Tomcat根目录下work文件夹的作用
- Memcached:高性能分布式对象缓存系统
- (实验小结三)master-slave 机制
- 浅析堡垒机概念及工作原理(转)