Struts2请求流程和原理(源码解析,运行流程)

      前言:此文章为本人复习Struts2的一个学习记录,若是有什么错误的地方能够留言讨论,当前Struts2更新到了2.5.16更新了很多东西,支持JSR303,debug标签只能在dev mode环境下面显示等等,看官们若是有须要能够移步下面连接由于本次测试不会涉及到这些:https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.5.16
web

      IDE: eclipse Neon ; Struts2 version : 2.3.16apache

    下面按照Eclipse Debug结合Struts2官方给出的流程图来进行一步步的解析,其中涉及到众多的类所以只会根据主流程来描述:服务器

Struts2-Architecture.png



第一步:HttpServletRequest进入到StrutsPrepareAndExecuteFilter(图上ActionContextCleanUp这个类在集成SiteMesh才有用的其他时候能够无论)

       流程图上的FilterDispacther即在web.xml中配置的filter:StrutsPrepareAndExecuteFilter这个也是Struts2的核心(下面提到的该类指向这个类)在Struts项目中全部的请求都须要通过它的处理。(注意:更早的版本会有所不一样)当每次启动服务器的时候第一次请求都会调用该类的init方法,随后的请求会调用doFilter。如图一咱们直接把断点打在doFilter中开始调试。app

图一:
eclipse



         根据官方的流程图能够看到请求经过FilterDispacther后会进入到ActionMapper,ActionMapper这个类是一个接口就是用来匹配当前的请求是否有对应的action。它有4个实现类分别是:RestfulActionMapper,DefaultActionMapper(默认使用),CompositeActionMapper,PrefixBasedActionMapper。
学习

         经过ActionMapper获得ActionMapping这个类(图二),ActionMapping对象是用来存放ActionMapper(默认DefaultActionMapper)解析后获得的name,nameSpace,method,extension,params,result 具体能够查看官方文档这边再也不赘述。根据图三能够清楚的看到getMapping这个方法就是解析咱们的请求来获得ActionMapping,值得一提的是在这个方法中最后调用到的方法parseActionName(图四)有个if判断条件为allowDynamicMethodCalls,这个值的意思就是说是否容许请求使用Struts DMI(动态方法调用例如访问的形式为test!test,意思就是访问test这个对应action中的test方法),Struts配置文件default.properties中默认设置为false,若是你要容许使用DMI就在struts.xml中添加<constant name="struts.enable.DynamicMethodInvocation" value="true"/>。最后经过这个方法后咱们获得了ActionMapping。
测试

图二:
spa


图三:debug


图四:3d


第二步:StrutsPrepareAndExecuteFilter调用dispatcher类的serviceAction建立ActionProxy

         获得ActionMapping后接着执行execute.executeAction方法。此时StrutsPrepareAndExecuteFilter会调用dispatcher这个类的serviceAction(图六),这个方法会对valuStatck等进行处理,重要的是这边会根据ActionMapping这个对象中的属性(前文提到)以及configurationManager(加载Struts2的配置文件)来生成一个ActionProxy若是这个ActionProxy成功建立的话会执行execute方法(图七)反之若是匹配不到Action即没法生成相应的代理对象会返回404页面。

图五:

图六:


图七:


第三步:经过ActionProxy对象调用Actioninvocation的invoke方法执行全部的拦截器

      在ActionProxy的execute方法中最后执行的是Actioninvocation(建立ActionProxy过程当中默认生成的是它的子类DefaultActionInvocation)的invoke方法。从图八中能够看到从Strtus2从这里开始对拦截器进行处理。这边的interceptors集合在初始化ActionProxy的时候经过配置文件(struts的Struts-default.xml中<interceptor-stack name="defaultStack">)获得。在执行图八中intercept方法时候Struts会把全部的拦截器执行一遍,接着执行请求所指的action中的方法而且按照后进先出的规则执行先前的拦截器(可查看官方流程图)。


      下面咱们来分析下这个过程。首先经过interceptors.next()获取配置文件中的ExceptionMappingInterceptor对象(这个时候interceptors集合的指针下移也就是说下一次的interceptors.next()指向了AliasInterceptor对象)接着调用ExceptionMappingInterceptor的intercept方法并把当前的DefaultActionInvocation对象做为参数传递进去。而在intercept中又调用了DefaultActionInvocation的invoke方法(图九)接着在剩下的全部拦截器中重复调用Invoke方法直到把全部的拦截器调用完毕。


图八:


图九:

第四步:执行HttpServletRequest对应的Action方法并返回Result

         当到了最后一个拦截器DeprecationInterceptor(默认配置状况下最后一个)的时候,此时interceptors.HasNext()为false,所以执行invokeActionOnly方法(图八)。这个方法是用来执行ActionProxy代理的那个Action方法(本次事例为test!add所以对应的Action方法就是add)并得到Result的过程,该过程用了Java的反射机制来实现(图十)。最后按照后进先出的方式依次返回全部拦截器的值最终返回Result视图。

图十:

        至此整个Struts2运行流程到此就算是结束了,上述只是这个流程的主要一部分,其中还涉及到了许多的内容这边再也不详细的去探讨。感谢阅读,但愿读者可以看懂,有所疑问能够留言。