对于Struts2源代码的分析已经有些时日了,虽然网上有很多解读代码,不过自己还是写一个放上来,供大家参考一下。
解读过程:
直接在action类中打断点(包括构造函数和待执行方法)进行debug调试,查看调用栈即可找到整个执行过程,下面用一张图来表述。
说明一下:
1.下面样例对应Action类为UserAction,在步骤9创建,待调用的方法为addUser
2.因为默认拦截器比较多,下面样例只列了两个拦截器,ExceptionMappingInterceptor和DebuggingInterceptor,限于篇幅,其他的拦截器没有列出;
3.在StrutsSpringObjectFactory到生成UserAction对象之间还有一些类的调用,同样限于篇幅,就省略了。
下面的代码就是StrutsPrepareAndExecuteFilter的doFilter方法,有请求达到时,因为在web.xml配置了StrutsPrepareAndExecuteFilter作为过滤器,所以就会执行到doFilter方法中来,下面的调用过程大致如下:
1.将输入参数转为HttpServletRequest和HttpServletResponse对象
2.如果不符合过滤要求,则直接转由下一个过滤器进行处理
3.设置字符编码和本地化信息
4.创建Action执行上下文对象
5.对request对象进行封装处理
6.通过配置信息查找请求对应的actionMapping对象
7.执行action对象对应的方法,也就是上面流程图所展示的过程
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); } }
在调测过程中,特别留意了ParameterInterceptor这个拦截器,没有这个拦截器,页面的参数就无法设置到Action类中去。
看到网上有人说到自定义拦截器的时候,必须要关联defaultStack(<interceptor-ref name="defaultStack"/>),其实这没有清楚,为什么必须这样。
defaulStack中包含很多个拦截器,其实不是所有action都需要这么多拦截器的,说实话我没有尝试过到底哪种类型的操作需要哪些拦截器,如果只是需要完成页面的值设置到action中,则只需要自定义拦截器时关联ParameterInterceptor即可,即:
<interceptor-ref name="params">
<param name="excludeParams">^action:.*,^method:.*</param>
</interceptor-ref>
ParameterInterceptor设置值到Action中的具体代码如下:
外部代码调用intercept方法,然后调用到doIntercept方法,将值设置到Action中则是通过下面代码中的setParameters(action, stack, parameters);这一行完成的。
public String intercept(ActionInvocation invocation) throws Exception { if (applyInterceptor(invocation)) { return doIntercept(invocation); } return invocation.invoke(); }
public String doIntercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); if (!(action instanceof NoParameters)) { ActionContext ac = invocation.getInvocationContext(); final Map<String, Object> parameters = retrieveParameters(ac); if (LOG.isDebugEnabled()) { LOG.debug("Setting params " + getParameterLogMap(parameters)); } if (parameters != null) { Map<String, Object> contextMap = ac.getContextMap(); try { ReflectionContextState.setCreatingNullObjects(contextMap, true); ReflectionContextState.setDenyMethodExecution(contextMap, true); ReflectionContextState.setReportingConversionErrors(contextMap, true); ValueStack stack = ac.getValueStack(); setParameters(action, stack, parameters); } finally { ReflectionContextState.setCreatingNullObjects(contextMap, false); ReflectionContextState.setDenyMethodExecution(contextMap, false); ReflectionContextState.setReportingConversionErrors(contextMap, false); } } } return invocation.invoke(); }