本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188
首先我们看一下struts官方给我们提供的struts执行流程
从上面流程图我们可以看出struts执行的流程大体分一下阶段:
1. 初始的请求通过一条标准的过滤器链,到达servlet 容器( 比如tomcat 容器,WebSphere 容器)。
2. 过滤器链包括可选的ActionContextCleanUp 过滤器,用于系统整合技术,如SiteMesh 插件。
3. 接着调用FilterDispatcher,FilterDispatcher 查找ActionMapper,以确定这个请求是否需要调用某个Action。
4. 如果ActionMapper 确定需要调用某个Action,FilterDispatcher 将控制权交给ActionProxy。
5. ActionProxy 依照框架的配置文件(struts.xml),找到需要调用的Action 类。
6. ActionProxy 创建一个ActionInvocation 的实例。ActionInvocation 先调用相关的拦截器(Action 调用之前的部分),最后调用Action。
7. 一旦Action 调用返回结果,ActionInvocation 根据struts.xml 配置文件,查找对应的转发路径。返回结果通常是(但不总是,也可能是另外的一个Action 链)JSP 技术或者FreeMarker的模版技术的网页呈现。Struts2 的标签和其他视图层组件,帮助呈现我们所需要的显示结果。在此,我想说清楚一些,最终的显示结果一定是HTML 标签。标签库技术和其他视图层技术只是为了动态生成HTML 标签。
8. 接着按照相反次序执行拦截器链( 执行Action 调用之后的部分)。最后,响应通过滤器链返回(过滤器技术执行流程与拦截器一样,都是先执行前面部分,后执行后面部)。如果过滤器链中存在ActionContextCleanUp,FilterDispatcher 不会清理线程局部的ActionContext。如果不存在ActionContextCleanUp 过滤器,FilterDispatcher 会清除所有线程局部变量。
下面我们就来具体分析一下3-6四个步骤:
步骤三:FilterDispatcher 查找ActionMapper,以确定这个请求是否需要调用某个Action。
1)
ActionMapping mapping; try { mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); } catch (Exception ex) { log.error("error getting ActionMapping", ex); dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); return; }
2)调用actionmapper去寻找对应的ActionMapping,因为actionmapper是一个接口,所有我们去他对应的实现类(DefaultActionMapper)里面去找getMapping方法,下面我们来看一下实现类里面的getMapping方法源代码:
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) { ActionMapping mapping = new ActionMapping(); String uri = getUri(request); int indexOfSemicolon = uri.indexOf(";"); uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri; uri = dropExtension(uri, mapping); if (uri == null) { return null; } parseNameAndNamespace(uri, mapping, configManager); handleSpecialParameters(request, mapping); if (mapping.getName() == null) { return null; } parseActionName(mapping); return mapping; }
ActionMapping 代表struts.xml 文件中的一个Action 配置,被传入到serviceAction 中。注意ActionMapping 不代表Action 集合,只代表某个对应的Action。如果是一个Action 请求,( 请求路径在struts.xml 有对应的Action 配置,即actionmapping不为空),则调用dispatcher.serviceAction() 处理。找到对应的ActionMapping,下一步就去找具体的执行哪一个action,从FilterDispatcher源码中我们可以找到下一步流程:
dispatcher.serviceAction(request, response, servletContext, mapping);
从上面可以看出,FilterDispatcher类中是调用的serviceAction方法来寻找的去调用哪一个action。serviceAction()方法作用:加载Action 类,调用Action 类的方法,转向到响应结果。响应结果指<result/> 标签所代表的对象。
步骤四、五、六:如果ActionMapper 确定需要调用某个Action,FilterDispatcher 将控制权交给ActionProxy。
我们来看一下具体的serviceAction源码:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { Map<String, Object> extraContext = createContextMap (request, response, mapping, context); //1 以下代码目的为获取ValueStack,代理在调用的时候使用的是本值栈的副本 ValueStack stack = (ValueStack) request.getAttribute (ServletActionContext.STRUTS_VALUESTACK_KEY); boolean nullStack = stack == null; if (nullStack) { ActionContext ctx = ActionContext.getContext(); if (ctx != null) { stack = ctx.getValueStack(); } } //2 创建ValueStack 的副本 if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); //3 这个是获取配置文件中<action/> 配置的字符串,action 对象已经在核心控制器中创建 String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); // xwork 的配置信息 Configuration config = configurationManager.getConfiguration(); //4 动态创建ActionProxy ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class). createActionProxy(namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); //5 调用代理 if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { proxy.execute(); } //6 处理结束后,恢复值栈的代理调用前状态 if (!nullStack) { request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { //7 如果action 或者result 没有找到,调用sendError 报404 错误 }
关于valuestack说明一下:
1.valueStack 的建立是在doFilter 的开始部分,在Action 处理之前。即使访问静态资源ValueStack 依然会建立,保存在request 作用域。
2. ValueStack 在逻辑上包含2 个部分:object stack 和context map,object stack 包含Action 与Action 相关的对象。
context map 包含各种映射关系。request,session,application,attr,parameters 都保存在context map 里。
parameters: 请求参数
atrr: 依次搜索page, request, session, 最后application 作用域。
几点说明:
1. Valuestack 对象保存在request 里,对应的key 是ServletActionContext.STRUTS_VALUESTACK_KEY。调用代理之前首先创建Valuestack 副本,调用代理时使用副本,调用后使用原实例恢复。本处的值栈指object stack。
2. Dispatcher 实例,创建一个Action 代理对象。并把处理委托给代理对象的execute 方法。
最后我们在一起看一下ActionInvocation实现类中invoke方法执行的流程:invoke源代码:
public String invoke() throws Exception { String profileKey = "invoke: "; try { UtilTimerStack.push(profileKey); if (executed) { throw new IllegalStateException("Action has already executed"); } if (interceptors.hasNext()) { final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next(); String interceptorMsg = "interceptor: " + interceptor.getName(); UtilTimerStack.push(interceptorMsg); try { resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); } finally { UtilTimerStack.pop(interceptorMsg); } } else { resultCode = invokeActionOnly(); } if (!executed) { if (preResultListeners != null) { for (Object preResultListener : preResultListeners) { PreResultListener listener = (PreResultListener) preResultListener; String _profileKey = "preResultListener: "; try { UtilTimerStack.push(_profileKey); listener.beforeResult(this, resultCode); } finally { UtilTimerStack.pop(_profileKey); } } } if (proxy.getExecuteResult()) { executeResult(); } executed = true; } return resultCode; } finally { UtilTimerStack.pop(profileKey); } }
这里算是执行action中方法的最后一步了吧,至此,action的整个流程就基本差不多了,从头到尾看下来,说实话,感触很多,很多不明白的地方,这算是近了自己最大的努力去看这些源码,感觉从里面收获了很多,里面很多的机制和知识点值得我们去学习,记住了圣思源张龙老师的那句话:源码面前,一目了然