Struts2的请求的执行步骤:
-----------------------------------------------------------
①.客户端发送请求;
②.该请求经过一系列的过滤器(Filter):其中可选过滤器ActionContextCleanUp,帮助Struts2和其他框架集成。例如:SiteMeshPlugin。
③.接着FilterDispatcher前段过滤器被调用,FilterDispatcher询问ActionMapper,来决定该请求是否需要调用某个Action。
④.若ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy。
⑤.ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类。
⑥.ActionProxy创建一个ActionInvocation的实例。
⑦.ActionInvocation实例调用Action的前后,涉及到相关拦截器(Intercepter)的调用。
⑧.一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果是一个JSP或其他页面(也可以是其他的Action链)。JSP页面展现可使用Struts2框架中的标签(该过程会涉及ActionMapper)。
在 上述过程中所有的对象(Action、Interceptors、Results等)都由 xwork容器中的ObjectFactory创建拦截器:Interceptor
------------------------------
拦截器:Struts2拦截器是在访问某个Action或Action的某个方法之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.
AOP:面向切面编程.其实现原理:动态代理模式--->留给Spring
WebWork中文文档解释:拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个Action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也提供了一种可以提取Action中可重用的代码的方式。
拦截器栈(InterceptorStack):Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
-------------------------------------------------------------------------
拦截器的"美":
---------------------------------------------------
DRY原则:Dont'tRepeat Yourself.
拦截器在设计和程序结构上的优点:
拦截器能把很多功能从Action中独立出来,分散到不同的拦截器里面,减少了Action的代码。如此,拦截器和Action本身的功能都更单一了。当通用的功能代码被封装在拦截器里面(代码模块化),就可以对不同的Action,根据功能需要,来配置相应功能的拦截器了。提高了拦截器所实现的功能的重用性,也变相实现了装配式和可插拔式的体系结构,使得整个系统结构变得更灵活。
1.简化Action的实现
2.功能更单一
3.通用代码模块化
4.提高重用性
下面我们就来具体分析一下3-6四个步骤:
步骤三:FilterDispatcher 或者说 StrutsPrepareAndExecuteFilter 查找ActionMapper,以确定这个请求是否需要调用某个Action。
1)
[java] view plain copy
1. ActionMapping mapping;
2. try {
3.
4. mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
5.
6. } catch (Exception ex) {
7.
8. log.error("error getting ActionMapping", ex);
9.
10. dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
11.
12. return;
13. }
2)调用actionmapper去寻找对应的ActionMapping,因为actionmapper是一个接口,所有我们去他对应的实现类(DefaultActionMapper)里面去找getMapping方法,下面我们来看一下实现类里面的getMapping方法源代码:
[java] view plain copy
1. public ActionMapping getMapping(HttpServletRequest request,
2.
3. ConfigurationManager configManager) {
4.
5. ActionMapping mapping = new ActionMapping();
6.
7. String uri = getUri(request);
8. int indexOfSemicolon = uri.indexOf(";");
9.
10. uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;
11.
12. uri = dropExtension(uri, mapping);
13.
14. if (uri == null) {
15.
16. return null;
17.
18. }
19.
20. parseNameAndNamespace(uri, mapping, configManager);
21.
22. handleSpecialParameters(request, mapping);
23.
24. if (mapping.getName() == null) {
25.
26. return null;
27.
28. }
29. parseActionName(mapping);
30.
31. return mapping;
32. }
ActionMapping 代表struts.xml 文件中的一个Action 配置,被传入到serviceAction 中。注意ActionMapping 不代表Action 集合,只代表某个对应的Action。如果是一个Action 请求,( 请求路径在struts.xml 有对应的Action 配置,即actionmapping不为空),则调用dispatcher.serviceAction() 处理。找到对应的ActionMapping,下一步就去找具体的执行哪一个action,从FilterDispatcher源码中我们可以找到下一步流程:
[java] view plain copy
1. dispatcher.serviceAction(request, response, servletContext, mapping);
从上面可以看出,FilterDispatcher类中是调用的serviceAction方法来寻找的去调用哪一个action。serviceAction()方法作用:加载Action 类,调用Action 类的方法,转向到响应结果。响应结果指<result/> 标签所代表的对象。
步骤四、五、六:如果ActionMapper 确定需要调用某个Action,FilterDispatcher 将控制权交给ActionProxy。
我们来看一下具体的serviceAction源码:
[java] view plain copy
1. public void serviceAction(HttpServletRequest request, HttpServletResponse response,
2.
3. ServletContext context, ActionMapping mapping) throws ServletException {
4.
5. Map<String, Object> extraContext = createContextMap
6.
7. (request, response, mapping, context);
8.
9. //1 以下代码目的为获取ValueStack,代理在调用的时候使用的是本值栈的副本
10.
11. ValueStack stack = (ValueStack) request.getAttribute
12.
13. (ServletActionContext.STRUTS_VALUESTACK_KEY);
14.
15. boolean nullStack = stack == null;
16.
17. if (nullStack) {
18.
19. ActionContext ctx = ActionContext.getContext();
20.
21. if (ctx != null) {
22.
23. stack = ctx.getValueStack();
24.
25. }
26.
27. }
28.
29. //2 创建ValueStack 的副本
30.
31. if (stack != null) {
32.
33. extraContext.put(ActionContext.VALUE_STACK,
34.
35. valueStackFactory.createValueStack(stack));
36.
37. }
38.
39. String timerKey = "Handling request from Dispatcher";
40.
41. try {
42.
43. UtilTimerStack.push(timerKey);
44.
45. //3 这个是获取配置文件中<action/> 配置的字符串,action 对象已经在核心控制器中创建
46.
47. String namespace = mapping.getNamespace();
48.
49. String name = mapping.getName();
50.
51. String method = mapping.getMethod();
52.
53. // xwork 的配置信息
54.
55. Configuration config = configurationManager.getConfiguration();
56.
57. //4 动态创建ActionProxy
58.
59. ActionProxy proxy =
60.
61. config.getContainer().getInstance(ActionProxyFactory.class).
62.
63. createActionProxy(namespace, name, method, extraContext, true, false);
64.
65. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY,
66.
67. proxy.getInvocation().getStack());
68.
69. //5 调用代理
70.
71. if (mapping.getResult() != null) {
72.
73. Result result = mapping.getResult();
74.
75. result.execute(proxy.getInvocation());
76.
77. } else {
78.
79. proxy.execute();
80.
81. }
82.
83. //6 处理结束后,恢复值栈的代理调用前状态
84.
85. if (!nullStack) {
86.
87. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
88.
89. }
90.
91. } catch (ConfigurationException e) {
92.
93. //7 如果action 或者result 没有找到,调用sendError 报404 错误
94.
95. }
关于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源代码:
[java] view plain copy
1. public String invoke() throws Exception {
2.
3. String profileKey = "invoke: ";
4.
5. try {
6.
7. UtilTimerStack.push(profileKey);
8.
9. if (executed) {
10.
11. throw new IllegalStateException("Action has already executed");
12.
13. }
14.
15. if (interceptors.hasNext()) {
16.
17. final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
18.
19. String interceptorMsg = "interceptor: " + interceptor.getName();
20.
21. UtilTimerStack.push(interceptorMsg);
22.
23. try {
24.
25. resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
26.
27. }
28.
29. finally {
30.
31. UtilTimerStack.pop(interceptorMsg);
32.
33. }
34.
35. } else {
36.
37. resultCode = invokeActionOnly();
38.
39. }
40.
41.
42. if (!executed) {
43.
44. if (preResultListeners != null) {
45.
46. for (Object preResultListener : preResultListeners) {
47.
48. PreResultListener listener = (PreResultListener) preResultListener;
49.
50.
51. String _profileKey = "preResultListener: ";
52.
53. try {
54.
55. UtilTimerStack.push(_profileKey);
56.
57. listener.beforeResult(this, resultCode);
58.
59. }
60.
61. finally {
62.
63. UtilTimerStack.pop(_profileKey);
64.
65. }
66.
67. }
68.
69. }
70.
71.
72. if (proxy.getExecuteResult()) {
73.
74. executeResult();
75.
76. }
77.
78.
79. executed = true;
80.
81. }
82. return resultCode;
83.
84. }
85.
86. finally {
87.
88. UtilTimerStack.pop(profileKey);
89.
90. }
91. }
这里算是执行action中方法的最后一步了吧,至此,action的整个流程就基本差不多了,从头到尾看下来,说实话,感触很多,很多不明白的地方,这算是近了自己最大的努力去看这些源码,感觉从里面收获了很多,里面很多的机制和知识点值得我们去学习。