Struts2

来源:互联网 发布:java 线程挂起与恢复 编辑:程序博客网 时间:2024/05/23 09:40
一:背景
struts2 的前身是webwork,WebWord的前身是Xwork,Xwork的延伸是命令模式.


Struts 2以WebWork优秀的设计思想为核心,吸收了Struts 1的部分优点,建立了一个兼容WebWork和Struts 1的MVC框架。


Struts 2的目标是希望可以让原来使用Struts 1、WebWork的开发人员,都可以平稳过渡到使用Struts 2框架。


二:struts2 的工作流程及其原理 Struts2简单请求流程介绍
(参考 有详细源代码分析:http://www.javaeye.com/topic/450979)


1、客户端发送请求


2、请求先通过ActionContextCleanUp-->FilterDispatcher 


3、FilterDispatcher通过ActionMapper来决定这个Request需要调用哪个Action 


4、如果ActionMapper决定调用某个Action,FilterDispatcher把请求的处理交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行 


5、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类 


6、ActionProxy创建一个ActionInvocation的实例 


7、ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用 


8、Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现,


Result对象可以是jsp,freemarker 等等表现层的技术。


三、struts2与struts1的区别
  — 在Action实现类方面的对比:


         Struts 1要求Action类继承一个抽象基类;Struts 1的一个具体问题是使用抽象类编程而不是接口。Struts 2 Action类可以实现一个Action接口,也可以实现其他接口,使可选


        和定制的服务成为可能。Struts 2提供一个ActionSupport基类去实现常用的接口。即使Action接口不是必须实现的,只有一个包含execute方法的POJO类都可以用 作Struts


        2的Action。
----- 线程模式方面的对比:


        Struts 1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts 1 Action能做的事,并且要在开发


        时特别小心。Action资源必须是线程安全的或同步的;Struts 2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。


----  Servlet依赖方面的对比:


       Struts 1 Action依赖于Servlet API,因为Struts 1 Action的execute方法中有HttpServletRequest和HttpServletResponse参数。Struts 2 Action不再依


       赖于Servlet API,从而允许Action脱离Web容器运行,从而降低了测试Action的难度。 当然,如果Action需要直接访问HttpServletRequest和


       HttpServletResponse参数,Struts 2 Action仍然可以访问它们。但是,大部分时候,Action都无需直接访问HttpServetRequest和 HttpServletResponse,从 


       而给开发者更多灵活的选择。


— 可测性方面的对比:


       测试Struts 1 Action的一个主要问题是execute方法依赖于Servlet API,这使得Action的测试要依赖于Web容器。为了脱离Web容器测试Struts 1的Action,


       必须借助于第三方扩展:Struts TestCase,该扩展下包含了系列的Mock对象(模拟了HttpServetRequest和HttpServletResponse对象),从而 可以脱离


       Web容器测试Struts 1的Action类。Struts 2 Action可以通过初始化、设置属性、调用方法来测试。


— 封装请求参数的对比:


       Struts 1使用ActionForm对象封装用户的请求参数,所有的ActionForm必须继承一个基类:ActionForm。普通的JavaBean不能用 作ActionForm,因此,开发


      者必须创建大量的ActionForm类封装用户请求参数。虽然Struts 1提供了动态ActionForm来简化ActionForm的开发,但依然需要在配置文件中定


     ActionForm;Struts 2直接使用Action属性来封装用户请求属性,避免了开发者需要大量开发ActionForm类的烦琐,实际上,这些属性还可以是包含子属性的  


     Rich 对象类型。如果开发者依然怀念Struts 1 ActionForm的模式,Struts 2提供了ModelDriven模式,可以让开发者使用单独的Model对象来封装用户请求参 


       数,但该Model对象无需继承任何Struts 2基类,是一个POJO,从而降低了代码污染。


— 表达式语言方面的对比:


      Struts 1整合了JSTL,因此可以使用JSTL表达式语言。这种表达式语言有基本对象图遍历,但在对集合和索引属性的支持上则功能不强;Struts 2可以使用


      JSTL,但它整合了一种更强大和灵活的表达式语言:OGNL(Object-Graph Navigation Language),因此,Struts 2下的表达式语言功能更加强大。


— 绑定值到视图的对比:


      Struts 1使用标准JSP机制把对象绑定到视图页面;Struts 2使用“ValueStack”技术,使标签库能够访问值,而不需要把对象和视图页面绑定在一起。


— 类型转换的对比:


       Struts 1 ActionForm 属性通常都是String类型。Struts 1使用Commons-Beanutils进行类型转换,每个类一个转换器,转换器是不可配置的;Struts 2使OGNL


       进行类型转换,支持基本数据类型和常用对象之间的转换。


— 数据校验的对比:


       Struts 1支持在ActionForm重写validate方法中手动校验,或者通过整合Commons alidator框架来完成数据校验。Struts 2支持通过重写validate方法进行校验,


       也支持整合XWork校验框架进行校验。


---  Action执行控制的对比:


       Struts 1支持每一个模块对应一个请求处理(即生命周期的概念),但是模块中的所有Action必须共享相同的生命周期。Struts 2支持通过拦截器堆栈


     (Interceptor Stacks)为每一个Action创建不同的生命周期。开发者可以根据需要创建相应堆栈,从而和不同的Action一起使用。


四;  WebWork和Struts 2对比(面试话题 一点点扩展)
          从某种程度上来看,Struts 2是WebWork的升级,而不是Struts 1的升级,甚至在Apache的Struts 2的官方文档都提到:WebWork到Struts 2是一次平滑的过渡。实际


上,Struts 2.0其实是WebWork 2.3而已,从WebWork 2.2迁移到Struts 2.0不会比从WebWork 2.1到2.2更麻烦。在很多方面,Struts 2仅仅是改变了WebWork下的名称,因此,


如果开发者具有WebWork的开发经验,将可以更加迅速地进入Struts 2的开发领域。下面是Struts 2与WebWork命名上存在改变(见表1.1):


表1.1  Struts 2和WebWork成员名称的对应


Struts 2成员


Struts 2成员 WebWork成员
com.opensymphony.xwork2.* com.opensymphony.xwork.*
org.apache.Struts2.* com.opensymphony.webwork.*
struts.xml xwork.xml
struts.properties webwork.properties
Dispatcher DispatcherUtil
org.apache.Struts2.config.Settings com.opensymphony.webwork.config.Configuration
除此之外,Struts 2也删除了WebWork中少量特性:
---  AroundInterceptor:
     Struts 2不再支持WebWork中的AroundInterceptor。如果应用程序中需要使用AroundInterceptor,则应该自己手动导入WebWork中的AroundInterceptor类。


— 富文本编辑器标签:
     Struts 2不再支持WebWork的富文本编辑器,如果应用中需要使用富文本编辑器,则应该使用Dojo的富文本编辑器。


---  IoC容器支持:
     Struts 2不再支持内建的IoC容器,而改为全面支持Spring的IoC容器,以Spring的IoC容器作为默认的Object工厂。


五、struts2的属性驱动和模型驱动:
Struts2的属性驱动指的是在action中JSP页面的每一个form中的name都对应在action中有一个属性与之对应。


当表单提交到Action后,Struts2将会自动将根据表单的name属性调用Action中相应的属性setter,去自动赋值。


Struts2的模型驱动,它需要一个pojo对象封装表单属性,然后action类必须要实现ModelDriven接口,并重写getModel方法,


同时Action提供一个这个封装表单属性的对象(私有),并提供相应的getter与setter,


这样在表单数据提交到action时,action就会自动将表单的属性值赋值到这个pojo对象中,模型驱动的好处就是使得struts2的action类里


更加清晰,更好地体现类的单一职责原则


六、OGNL表达式:
OGNL,全称是Object-Graph Navigation Language,中文意思就是对象图导航语言,它是一种功能强大的表达式语言(Expression Language,简称为EL),通过它简单一致的


表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。Struts2.x中使用OGNL


取代原来的EL来做界面数据绑定,所谓界面数据绑定,也就是把界面元素(例如一个textfield,hidden)和对象层某个类的某个属性绑定在一起,修改和显示自动同步这样就可以支


持在jsp页面来进行对象方法调用,如MyClass.doSomething();它还支持类静态的方法调用和值访问,调用静态方法的表达式或值的格式为@[类全名(包括包路径)]@[方法名|值


名]例如调用静态方法:@java.lang.String@format('foo %s', 'bar');调用静态属性值:@tutorial.MyConstant@APP_NAME;Struts2使用ognl的上下文环境中,包含


request,session,application等servlet对象的Map封装,所以可以直接通过#符号来对这些变量的值进行访问,


而这样做最大地好处就是降低了servlet与action之间的耦合度,从而struts2就弥补了struts1难以调试的缺陷,


如:


七、优缺点:        
1、简化设计,类与类之间是松散的耦合;


2、简化Action,Struts2中Action摇身变为普通的JAVA类,任何具有execute()方法的类都可以作为Struts2的Action,不必实现某个接口、继承某个类;


3、取消了ActionForm类,Action可以直接获得用户参数;


4、易于测试;


5、智能的默认配置;


6、改进返回值,不同于ActionForwards,Struts2的Action能返回多种类型的数据;


7、Struts2的标签库提供了对JSF、Freemarkerdede的支持;


8、快速启动;


9、与Spring整合;


10、支持Ajax;八. Struts 2的标签库
参考网址:http://su3390.blog.51cto.com/176528/41190


      标签:标签使我们开发JSP画面的时候使用的最小组件单元,一小段代码,在JSP,Velocity或者FreeMarker中执行。程序开发的最小单位,用来生成HTML对应


     的元素我们根据客户的需要组合各种Tag达到客户的需求  


     1:通用标签:用来在页面表示的时候控制代码执的过程,这些标签也允许从Action或者值堆栈中取得数据    


    * 控制标签控制程序执行,例如:if,append,iterator      


     If标签:用来控制基本的条件处理流程,通常和else标签或者elseif标签连用        


      append标签:用来做iterator标签的辅助,将不同iterator中的内容合在一个iterator中    


       iterator标签:迭代处理一个java.util.Connection或者java.util.Iterator对象      


     * 数据标签管理数据的取得和创建,例如:bean,push,i18n       


      il8n:描述:加载资源包到值堆栈。它可以允许text标志访问任何资源包的信息,而不只当前action相关联的资源包。  


      2:UI标签:UI标签主要是指Form相关的标签,每一个UI标签都是基于模板的,即:每一个标签都有一个对应的模板用来生成UI标签的样式         


九.Struts 2的控制器组件
参考网址:http://hi.baidu.com/hjysuccess/blog/item/5e7727601409c041ebf8f8f7.html  


 1:FilterDispatcher(核心控制器)     FilterDispatcher 是struts2框架的核心控制器,该控制器作为一个Filter运行在Web应用中,它负责拦截所有用户请求,当用户请求到达


时,该Filter会过滤 用户请求。如果用户请求以.action结尾,该请求将被转入struts2框架处理。   


  里面主要有三个方法:          Init():初始化filter方法         destroy():销毁filter方法         doFilter():在此方法中,将调用dispatcher.serviceAction,该方法如果找到相应的


Action,将把用户请求交给ActionProxy       


2:Action(业务控制器)      业务控制器Action是由开发者自己编写实现的,Action类可以是一个简单的Java类,无需实现任何父接


口,无需继承任何Struts2基类,与Servlet API完全分离。Action一般都有一个execute()方法,该方法返回一个字符串,该字符串就是一个逻辑视图名,每个字符串对应一个视图


名,在Action类里也可以定义其他业务控制方法     它的优点:     * Action类完全是一个POJO,因此具有很好的代码复用性。


     * Action类无需与Servlet API耦合,因此进行单元测试非常简单。


      * Action类的execute方法仅返回一个字符串作为处理结果,该处理结果可映射到任何的视图,甚至是另一个Action。                


十.struts2 Action 的优势
该Action类有如下优势:


---  Action类完全是一个POJO,因此具有很好的代码复用性。


---  Action类不与Servlet API耦合,因此进行单元测试非常简单。


---  Action类的execute方法仅返回一个字符串作为处理结果,该处理结果可映射到任何的视图,甚至是另一个Action。


十一.使用Struts 2的校验框架
一种验证就是采用xwork框架进行验证。首先,action类要extends ActionSupport


一种是使用AJAX 校验


十二.struts2 有哪几种主题
Struts2默认提供了四种主题:


Simple 主题:最简单的主题


simple主题是最简单的主题,它是最底层的结构,主要用于构建附加的功能或者行为(例如在此主题基础上进行扩展),使用simple主题时,每个UI标签只生成一个简单的HTML


元素,不会生成其他额外的内容。


Struts2的xhtml, css_xhtml主题都是对simple主题的包装和扩展。


XHTML     主题:默认主题,使用常用的HTML技巧


xhtml主题是Struts2的默认主题,它对simple主题进行扩展,在该主题的基础上增加了如下附加的特性:


1,针对HTML标签(如textfield和select标签)使用标准的两列表格布局。


2,每个HTML标签的Label,即可以出现在HTML元素的左边,也可以出现在上边,这取决于labelposition属性的设置。


3,自动输出校验错误信息。


4,输出JavaScript的客户端校验。


CSS XHTML主题: 使用CSS实现的XHTML主题


css_xhtml主题则对原有的xhtml主题进行了扩展,在xhtml主题基础上加入了CSS样式控制。


AJAX      主题:基于XHTML主题,但是同工了AJAX功能


ajax主题目对xhtml主题目进行了扩展,在xhtml主题上为每个标签提供了额外的Ajax支持。ajax主题的Ajax支持是以Dojo和DWR为基础的。ajax主题在xhtml主题基础上增加了如


下特性:


1,支持Ajax方式的客户端校验。


2,支持远程表单的异步提交(最好和submit标签一起使用)。


3,提供高级的div标签,允许实现局部更新部分HTML的功能。


4,提供高级的a标签,允许动态加载并执行远端的javaScript代码。


5,提供支持ajax的tabbedPanel。


6,提供"富客户端"模型的pub-sub事件模型。         


十三.struts2 的国际化    
struts2的国际化大致上分为
   页面的国际化,
   Action的国际化
   xml的国际化
首先在struts.properties文件中加入以下内容:


struts.custom.i18n.resources=messageResource


或在struts.xml中加入


<constant name="struts.custom.i18n.resources" value="messageResource"></constant> 


资源文件的命名格式: 名称_语言代码_国家代码. Properties


如果创建中文和英语国际化,那么资源文件名称为


messageResource_zh_CN.properties和messageResource_en_US.properties 


1. jsp页面的国际化


通过使用标签输出国际化


label.helloWorld为资源文件中定义的key  label.helloWorld=你好,世界


在messageResource_en_US.properties加入以下内容


  label.hello=hello{0}


  label.helloWorld=hello,world


  在messageResource_zh_CN.properties加入以下内容


  label.hello=你好{0}


(1). 上面两个都为输出一个hello word的两种表示


显示一个文本框,文本框的标题进行国际化


(2). 使用标签指定从某个特定的资源文件中取数据


指定在从messageResource取资源


(3).  callan使用带参数的资源.可以替换label.hello=hello{0}中的{0}
2. Action的国际化


Action的国际化主要是通过getText(String key)方法实现的


Java代码 


 
 public String execute() throws Exception { 
      // getText(String) string为key 
       String str1 = getText("label.helloWorld");  
       System.out.println(str1);  
       // 带参数的 
       String str2 = getText("label.hello",new String[]{"fjf"{color:black}});  #  
       System.out.println(str2); 
       // 与上一种实现一样 
       List l = new ArrayList();
       l.add("callan"); 
       String str3 = getText("label.hello",l);   
       System.out.println(str3);  
       return SUCCESS;
 } 
 3.  参数化国际化


在messageResource_en_US.properties加入以下内容
      userName=userName
      userName.required=${getText('userName')} is required


      在messageResource_zh_CN.properties加入以下内容
      userName=用户名
      userName.required=${getText('userName')} 不能为空
       
      在Action中
      String str4 = getText("userName.required");
      System.out.println(str4);
4. 使用校验框价时,提示信息可以国际化
  <field name="userName">
     <field-validator type="requiredstring">
         <message key=”userName.required”> </message>
     </field-validator>
  </field>
国际化资源文件分为三种级别


(1) --全局资源文件,可以被整个应该程序引用,它的资源文件放在src目录下,需要在struts.xml中定义常量声明, 在WEB-INF"classes目录下建立一个struts.properties文件,内容


如下:     struts.custom.i18n.resources=my 指定的文件在WEB-INF\classes目录下建立一个my.properties文件,当Struts2按着上述的顺序没有找到相应的属性文件时,最后就


会考虑寻找全局 的属性文件,因此,就会找到my.properties。


  ---局部的国际化资源文件分为包级别的、和类级别的。


        包级别和类级别的国际化资源文件均是放在包目录下,


        包级别的命名为:package_language_country.properties,作用范围为当前包中的所有类


        类级别的命名为:类名_language_country.properties,作用范围为当前类    ---当里面有同名的信息时,类级别的信息会覆盖包级别,包级别会覆盖全局的 


(2) 包级资源文件,每个包的根目录下可以新建资源文件,仅被当前包中的类访问.文件名格式为:package_语言代码_国家代码.  


(3) Action级资源文件,仅被当前Action引用,名称为action名_语言代码_国家代码 查找顺序为从小范围到大范围, Action级优先级最大 


网上资料(供参考)
   struts2 默认拦截器介绍(面试时候可以进行一些发散,觉得值得一看)


    附带网址:http://blog.csdn.net/jxplus/archive/2009/02/26/3938897.aspx


              Interceptor(拦截器)将Action共用的行为独立出来,在Action执行前后运行。这也就是我们所说的AOP(Aspect Oriented Programming,面向切面编程),它是分散


    关注的编程方法,它将通用需求功能从不相关类之中分离出来;同时,能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以。 


           Interceptor将很多功能从我们的Action中独立出来,大量减少了我们Action的代码,独立出来的行为具有很好的重用性。XWork、WebWork的许多功能都是有Interceptor实


    现,可以在配置文件中组装Action用到的Interceptor,它会按照你指定的顺序,在Action执行前后运行。Interceptor在框架中的应用如下图所示当你提交对Aciton(默认


    是.action结尾的Url)的请求时,ServletDispatcher会根据你的请求,去调度并执行相应的Action。在Action执行之前,调用被 Interceptor截取,Interceptor在


Action执行前后 运行。


           <interceptors>


            <interceptor name="timer" class="com.opensymphony.xwork.interceptor.TimerInterceptor"/>


            <interceptor name="logger" class="com.opensymphony.xwork.interceptor.LoggingInterceptor"/>


            <interceptor name="chain" class="com.opensymphony.xwork.interceptor.ChainingInterceptor"/>


            <interceptor name="static-params" class="com.opensymphony.xwork.interceptor.StaticParametersInterceptor"/>


            <interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>


            <interceptor name="model-driven" class="com.opensymphony.xwork.interceptor.ModelDrivenInterceptor"/>


            <interceptor name="component" class="com.opensymphony.xwork.interceptor.component.ComponentInterceptor"/>


            <interceptor name="token" class="com.opensymphony.webwork.interceptor.TokenInterceptor"/>


            <interceptor name="token-session" class="com.opensymphony.webwork.interceptor.TokenSessionStoreInterceptor"/>


            <interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>


            <interceptor name="workflow" class="com.opensymphony.xwork.interceptor.DefaultWorkflowInterceptor"/>


            <interceptor name="servlet-config" class="com.opensymphony.webwork.interceptor.ServletConfigInterceptor"/>


            <interceptor name="prepare" class="com.opensymphony.xwork.interceptor.PrepareInterceptor"/>


            <interceptor name="conversionError" class="com.opensymphony.webwork.interceptor.WebWorkConversionErrorInterceptor"/>


            <interceptor-stack name="defaultStack">


                <interceptor-ref name="static-params"/>


                <interceptor-ref name="params"/>


                <interceptor-ref name="conversionError"/>


            </interceptor-stack>


        </interceptors>


这些都时有框架提供的默认的Interceptor,下面我来看看Interceptor使用的步骤:


1、   创建一个自己需要的Interceptor类,它必需实现


com.opensymphony.xwork.interceptor.Interceptor接口,具体的开发见下面的Interceptor的原理。


2、   在配置文件(xwork..xml)中申明这个Interceptor类,它放在标签<interceptor />中,同是<interceptor />标签嵌入在<interceptors />标签内部。


3、   创建Interceptor栈,使用标签:<interceptor-stack />,让一组Interceptor可以按次序调用。(可选)


4、   指定Action所要用到的Interceptor(前面申明过的),可以用<interceptor-ref />或<default-interceptor-ref />标签。前面的标签指定某个Action所用到的Interceptor,如果


Action没有被用<interceptor-ref />指定Interceptor,它将使用<default-interceptor-ref />指定的Interceptor。


框架中给我们提供了很多实用的Interceptor,它的定义上面已经给出,它的具体功能如下:


l          timer:记录Action执行的时间,并做为日志信息输出;


l          logger:在日志信息中输出要执行的Action信息;


l          chain:将前一个执行结束的Action属性设置到当前的Action中。它被用在ResultType为“chain”指定结果的Action中,该结果Action对象会从OgnlValueStack中获得前一个


Action对应的属性,它实现Action链之间的数据传递;


l          static-params:将xwork.xml配置文件里定义的Action参数,设置到对应的Action中。Action参数使用<param />标签,是<action />标签的直接子元素。我们这里定义的


Action类必需实现com.opensymphony.xwork.config.entities. Parameterizable接口;


l          params:将Request请求的参数设置到相应Action对象的属性中,用户注册例子用到过这个拦截器;


l          model-driven:如果Action实现ModelDriven接口,它将getModel()取得的模型对象存入OgnlValueStack中;


l          component:激活组件功能支持,让注册过的组件在当前Action中可用,即为Action提供IoC(依赖倒转控制)框架的支持;


l          token:核对当前Action请求(request)的有效标识,防止重复提交Action请求(request)。


l          token-session:功能同上,但是当提交无效的Action请求标识时,它会将请求数据保存到session中。


l          validation:实现使用xml配置文件(


Unknown macro: {Action}
-validation.xml)对Action属性值进行验证,详细请看后面介绍的验证框架。


l          workflow:调用Action类的验证功能,假设Action使用ValidationAware实现验证(ActionSupport提供此功能),如果验证没有通过,workflow会将请求返回到input视图(Action的<result />中定义的)。
l          servlet-config:提供Action直接对HttpServletRequest或HttpServletResponse等JavaServlet api的访问,Action要实现相应的接口,例如:ServletRequestAware或


ServletResponseAware等。如果必需要提供对JavaServlet api的访问,我们建议使用ServletActionContext,在前面ActionContext章节中有介绍。


l          prepare:在Action执行之前调用Action的prepare()方法,这个方法是用来准备Action执行之前要做的工作。它要求我们的Action必需实现


com.opensymphony.xwork. Preparable接口


conversionError:用来处理框架进行类型转化(Type Conversion)时的出错信息。它将存储在ActionContext中的类型转化(Type Conversion)错误信息转化成相应的Action字段


的错误信息,保存在堆栈中。根据需要,可以将这些错误信息在视图中显示出来。


Interceptor的原理


下面我们来看看Interceptor是如何实现在Action执行前后调用的:


        Action和Interceptor在框架中的执行,是由ActionInvocation对象调用的。它是用方法:String invoke() throws Exception;来实现的,它首先会依次调用Action对应的


Interceptor,执行完成所有的Interceptor之后,再去调用Action的方法,代码如下:


if (interceptors.hasNext()) {
   Interceptor interceptor = (Interceptor) interceptors.next();
   resultCode = interceptor.intercept(this);
} else {
    if (proxy.getConfig().getMethodName() == null) {
    resultCode = getAction().execute();
} else {
        resultCode = invokeAction(getAction(), proxy.getConfig());
}
它会在拦截器栈中遍历Interceptor,调用Interceptor的方法:


String intercept(ActionInvocation invocation) throws Exception;。


我们一直都提到,Interceptor是在Action前后执行,可是从上面的代码我们看到的却是执行完所有Interceptor的intercept()方法之后再去调用我们的Action。“在Action前后执行”是


如何实现的呢?我们来看看抽象类AroundInterceptor的intercept()实现:


public String intercept(ActionInvocation invocation) throws Exception {
        String result = null;
        before(invocation);
        result = invocation.invoke();
        after(invocation, result);
        return result;
}
原来在intercept()方法又对ActionInvocation的invoke()方法进行递归调用,ActionInvocation循环嵌套在intercept()中,一直到语句result = invocation.invoke();执行结束,


即:Action执行完并返回结果result,这时Interceptor对象会按照刚开始执行的逆向顺序依次执行结束。这样before()方法将在Action执行前调用,after()方法在Action执行


之后运行