Struts执行流程和拦截器之美

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32048567/article/details/76795186

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)。

扫描二维码关注公众号,回复: 4771252 查看本文章
上述过程中所有的对象(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

 print?

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

 print?

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

 print?

1.  dispatcher.serviceAction(request, response, servletContext, mapping);  


    从上面可以看出,FilterDispatcher类中是调用的serviceAction方法来寻找的去调用哪一个action。serviceAction()方法作用:加载Action 类,调用Action 类的方法,转向到响应结果。响应结果指<result/> 标签所代表的对象。

步骤四、五、六:如果ActionMapper 确定需要调用某个ActionFilterDispatcher 将控制权交给ActionProxy

我们来看一下具体的serviceAction源码:

[java] view plain copy

 print?

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, truefalse);  

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

 print?

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的整个流程就基本差不多了,从头到尾看下来,说实话,感触很多,很多不明白的地方,这算是近了自己最大的努力去看这些源码,感觉从里面收获了很多,里面很多的机制和知识点值得我们去学习。

猜你喜欢

转载自blog.csdn.net/qq_32048567/article/details/76795186