struts2详解

Struts2的工作原理

                       

上图来源于Struts2官方站点,是Struts 2 的整体结构。


一个请求在Struts2框架中的处理大概分为以下几个步骤(可查看源码:https://github.com/apache/struts):
1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher(现已过时)被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper

在上述过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。

 

Struts开源架构很好的实现了MVC模式,MVC即Model-View-Controller的缩写,是一种常用的设计模式。MVC 减弱了业务逻辑接口和数据接口之间的耦合,以及让视图层更富于变化。MVC的工作原理,如下图1所示:

                       

Struts 是MVC的一种实现,它将 Servlet和 JSP 标记(属于 J2EE 规范)用作实现的一部分。Struts继承了MVC的各项特性,并根据J2EE的特点,做了相应的变化与扩展。下面是Struts实现MVC的原理。如下图2所示:

 

控制:通过图2大家可以看到有一个XML文件Struts-config.xml,与之相关联的是Controller, ,它可以称作为Struts神经中枢。

       视图:主要由JSP生成页面完成视图,Struts提供丰富的JSP 标签库: Html,Bean,Logic,Template等,这有利于分开在Struts中,承担MVC中Controller角色的是一个Servlet,叫ActionServlet。ActionServlet是一个通用的控制组件。这个控制组件提供了处理所有发送到Struts的HTTP请求的入口点。它截取和分发这些请求到相应的动作类(这些动作类都是Action类的子类)。另外控制组件也负责用相应的请求参数填充 Action From(通常称之为FromBean),并传给动作类(通常称之为ActionBean)。动作类实现核心商业逻辑,它可以访问java bean 或调用EJB。最后动作类把控制权传给后续的JSP 文件,后者生成视图。所有这些控制逻辑利用Struts-config.xml文件来配置。表现逻辑和程序逻辑。

       模型:模型以一个或多个java bean的形式存在。这些bean分为三类:Action Form、Action、JavaBean or EJB。Action Form通常称之为FormBean,封装了来自于Client的用户请求信息,如表单信息。Action通常称之为ActionBean,获取从ActionSevlet传来的FormBean,取出FormBean中的相关信息,并做出相关的处理,一般是调用Java Bean或EJB等。

       流程:在Struts中,用户的请求一般以*.do作为请求服务名,所有的*.do请求均被指向ActionSevlet,ActionSevlet根据Struts-config.xml中的配置信息,将用户请求封装成一个指定名称的FormBean,并将此FormBean传至指定名称的ActionBean,由ActionBean完成相应的业务操作,如文件操作,数据库操作等。每一个*.do均有对应的FormBean名称和ActionBean名称,这些在Struts-config.xml中配置。

 

struts2中struts.xml配置文件详解

struts.xml的常用配置 

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <!-- 所有匹配*.action的请求都由struts2处理 -->
    <constant name="struts.action.extension" value="action" />
    <!-- 是否启用开发模式 -->
    <constant name="struts.devMode" value="true" />
    <!-- struts配置文件改动后,是否重新加载 -->
    <constant name="struts.configuration.xml.reload" value="true" />
    <!-- 设置浏览器是否缓存静态内容 -->
    <constant name="struts.serve.static.browserCache" value="false" />
    <!-- 请求参数的编码方式 -->
    <constant name="struts.i18n.encoding" value="utf-8" />
    <!-- 每次HTTP请求系统都重新加载资源文件,有助于开发 -->
    <constant name="struts.i18n.reload" value="true" />
    <!-- 文件上传最大值 -->
    <constant name="struts.multipart.maxSize" value="104857600" />
    <!-- 让struts2支持动态方法调用 -->
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    <!-- Action名称中是否还是用斜线 -->
    <constant name="struts.enable.SlashesInActionNames" value="false" />
    <!-- 允许标签中使用表达式语法 -->
    <constant name="struts.tag.altSyntax" value="true" />
    <!-- 对于WebLogic,Orion,OC4J此属性应该设置成true -->
    <constant name="struts.dispatcher.parametersWorkaround" value="false" />

    <package name="basePackage" extends="struts-default">


    </package>

</struts>
复制代码

  

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd" >
<struts>

    <!-- include节点是struts2中组件化的方式 可以将每个功能模块独立到一个xml配置文件中 然后用include节点引用 -->
    <include file="struts-default.xml"></include>
    
    
    <!-- package提供了将多个Action组织为一个模块的方式
        package的名字必须是唯一的 package可以扩展 当一个package扩展自
        另一个package时该package会在本身配置的基础上加入扩展的package
        的配置 父package必须在子package前配置 
        name:package名称
        extends:继承的父package名称
        abstract:设置package的属性为抽象的 抽象的package不能定义action 值true:false
        namespace:定义package命名空间 该命名空间影响到url的地址,例如此命名空间为/test那么访问是的地址为http://localhost:8080/struts2/test/XX.action
     -->
    <package name="com.kay.struts2" extends="struts-default" namespace="/test">
        <interceptors>
            <!-- 定义拦截器 
                name:拦截器名称
                class:拦截器类路径
             -->
            <interceptor name="timer" class="com.kay.timer"></interceptor>
            <interceptor name="logger" class="com.kay.logger"></interceptor>
            <!-- 定义拦截器栈 -->
            <interceptor-stack name="mystack">
                <interceptor-ref name="timer"></interceptor-ref>
                <interceptor-ref name="logger"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        
        <!-- 定义默认的拦截器 每个Action都会自动引用
         如果Action中引用了其它的拦截器 默认的拦截器将无效 -->
        <default-interceptor-ref name="mystack"></default-interceptor-ref>
        
        
        <!-- 全局results配置 -->
        <global-results>
            <result name="input">/error.jsp</result>
        </global-results>
        
        <!-- Action配置 一个Action可以被多次映射(只要action配置中的name不同)
             name:action名称
             class: 对应的类的路径
             method: 调用Action中的方法名
        -->
        <action name="hello" class="com.kay.struts2.Action.LoginAction">
            <!-- 引用拦截器
                name:拦截器名称或拦截器栈名称
             -->
            <interceptor-ref name="timer"></interceptor-ref>
        
            <!-- 节点配置
                name : result名称 和Action中返回的值相同
                type : result类型 不写则选用superpackage的type struts-default.xml中的默认为dispatcher
             -->
         <result name="success" type="dispatcher">/talk.jsp</result>
         <!-- 参数设置 
             name:对应Action中的get/set方法 
         -->
         <param name="url">http://www.sina.com</param>
        </action>
    </package>
</struts>
复制代码

 

一个Action内包含多个请求处理方法的处理

Struts1提供了DispatchAction,从而允许一个Action内包含多个请求处理方法。Struts2也提供了类似的功能。

处理方式主要有以下三种方式:

 

1. 1   动态方法调用:

  DMI:Dynamic Method Invocation 动态方法调用。
  动态方法调用是指:表单元素的action不直接等于某个Action的名字,而是以感叹号后加方法名来指定对应的动作名:
    <!-- 动态方法调用HTML标签与Struts2标签 -->
        <form action="computeAction!add.action" name="from" >
        <s:form action="computeAction!add.action" name="form" theme="simple" >

  则用户的请求将提交到名为”computeAction”的Action实例,Action实例将调用名为”add”方法来处理请求。
  当指定调用某一方法来处理请求时,就不会走默认执行处理请求的execute()方法。

  注意:要使用动态方法调用,必须设置Struts2允许动态方法调用,通过设置struts.enable.DynamicMethodInvocation常量来完成,该常量属性的默认值是true。

复制代码
<struts>
    <!-- 
        //禁用动态方法调用,默认为true启用,false禁用
        constant:name="struts.enable.DynamicMethodInvocation"
     -->
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
</struts>
复制代码

 

示列:简单的一个加法和减法例子。

1. index.jsp用户在页面输入两个数字,选择相加,或者相减
   当用户点击加或减需要走同一个Action但处理请求方法不同,这里使用了js动态选择。

复制代码
<body>
    <!-- 动态方法调用    使用:Struts2标签也可以使用HTML标签 -->
        <s: name="form" theme="simple" >
        
            num1:<s:textfield name="num1" />
            num2:<s:textfield name="num2" />
            <s:submit type="button" value="加" onclick="computeMethod('add')" />
            <s:submit type="button" value="减" onclick="computeMethod('subtract')" />
        </s:form>
        
         
        
    <!-- js -->
    <script type="text/javascript">
        function computeMethod(op){
            document.form.action="computeAction!"+op;//动态选择处理请求的方法
            document.form.submit();//提交
        }
    </script>
            
  </body>
复制代码

 

2. struts.xml配置信息,启用动态方法调用(可选)

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" >
<struts>
    <!-- 
        //禁用动态方法调用,默认为true启用,false禁用
        constant:name="struts.enable.DynamicMethodInvocation"
     -->
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    <package name="struts2" extends="struts-default">
        <action name="computeAction" class="com.struts.ComputeAction" >
            <result name="fruitPage" >/fruit.jsp</result>
        </action>
    </package>
</struts>
复制代码

 

3. ComputeAction控制器的类处理请求

复制代码
package com.struts;
/**
 * Struts2控制器的类
 * @author asus
 *
 */
public class ComputeAction {

    /** 属性 */
    private int num1;
    private int num2;
    private int fruit;//结果
    
    /** 若请求为指定操作方法默认执行execute()方法 */
    public String execute(){
        
        System.out.println("当调用其它方法就不会走这个方法!");
        return "";
    }
    
    /** 执行处理加法 */
    public String add(){
        this.fruit=num1+num2;//加
        return "fruitPage";
    }
    
    /** 执行处理减法 */
    public String subtract(){
        this.fruit=num1-num2;//减
        return "fruitPage";
    }
    
    
    
    
    /** JavaBean */
    public int getNum1() {
        return num1;
    }

    public void setNum1(int num1) {
        this.num1 = num1;
    }

    public int getNum2() {
        return num2;
    }

    public void setNum2(int num2) {
        this.num2 = num2;
    }

    public int getFruit() {
        return fruit;
    }

    public void setFruit(int fruit) {
        this.fruit = fruit;
    }
    
}
复制代码

 

4. fruit.jsp响应结果的页面

<body>
            <!-- 结果页面 -->
            计算结果:<s:property value="fruit" />
</body>

 

1.2Action配置method属性(示列与以上代码大多一致,只修改有变更的):

  将Action类中的每一个处理方法都定义成一个逻辑Action方法。

1. index.jsp页面

复制代码
  <body>
  
    <!-- Action配置method属性    使用:Struts2标签也可以使用HTML标签 -->
        <s:form name="form" theme="simple" >
        
            num1:<s:textfield name="num1" />
            num2:<s:textfield name="num2" />
            <s:submit type="button" value="加" onclick="computeMethod('addAction')" />
            <s:submit type="button" value="减" onclick="computeMethod('subtractAction')" />
        </s:form>
        
         
        
    <!-- js -->
    <script type="text/javascript">
        function computeMethod(op){
            document.form.action=op;//动态选择处理请求的方法
            document.form.submit();//提交
        }
            
    </script>
            
  </body>
复制代码

 

2. struts.xml配置信息

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" >
<struts>
    <package name="struts2" extends="struts-default">
        <action name="addAction" class="com.struts.ComputeAction" method="add" >
            <result name="fruitPage" >/fruit.jsp</result>
        </action>
        <action name="subtractAction" class="com.struts.ComputeAction" method="subtract" >
            <result name="fruitPage" >/fruit.jsp</result>
        </action>
    </package>
</struts>
复制代码

  通过action元素的method属性来指定Action执行时调用的方法。

    优点:使得以更加安全的方式来实现动态方法的调用,不让别人看到你的实现方法。
    缺点:繁琐,一个处理请求的方法要跟一个action。

  Struts2根据method属性查找方法有两种途径:

        1.查找与method属性值完全一致的方法
        2.查找doMethod形式的方法

   使用动态方法调用和method属性的区别:

    1.通过以上三个struts.xml中的配置信息例子来说,他们的共同点是都在操作同一个Action。

    2.<form action="">中请求地址不同。

    3.动态方法的返回值相同,则会通过result进入一个页面。而method属性就算两个方法的返回值相同但进去不同的result,可能会进入两个不同的页面。

    由上可以分析出:
      (1)如果使用同一个Action,不同的处理请求的方法,响应使用相同的配置(result等)则使用动态方法调用。

      (2)如果使用同一个Action,不同的处理请求的方法,响应分别使用不同的配置,则使用action元素的method属性,为同一个Action配置多个名称。

      

 1.3使用通配符映射(wildcard mappings)方式(示列与以上代码大多一致,只修改有变更的):

1. index.jsp页面只改动了js部分。

复制代码
 <body>
    
    <!-- 使用通配符映射(wildcard mappings)方式    使用:Struts2标签也可以使用HTML标签 -->
        <s:form name="form" theme="simple" >
        
            num1:<s:textfield name="num1" />
            num2:<s:textfield name="num2" />
            <s:submit type="button" value="加" onclick="computeMethod('addAction')" />
            <s:submit type="button" value="减" onclick="computeMethod('subtractAction')" />
        </s:form>
        
         
        
    <!-- js -->
    <script type="text/javascript">
        function computeMethod(op){
            document.form.action=op;//相比mothod属性改动只有这里
            document.form.submit();//提交
        }
            
    </script>
            
  </body>
复制代码

 

 2.struts.xml的配置信息

  在使用method属性来实现同一个Action的不同方法处理不同的请求时,会发现,随着方法的增多,从而导致大量的Action配置,这时我们就需要通过使用通配符来解决Action配置过多的方法。

  在配置<action.../>元素时,需要指定name、class、method属性。其中name属性可支持通配符,然后可以在class、method属性中使用表达式。通配符用星号 * 表示。

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" >
<struts>
    <package name="struts2" extends="struts-default">
    
        <action name="*Action" class="com.struts.ComputeAction" method="{1}" >
            <result name="fruitPage" >/fruit.jsp</result>
            <!-- <result name="fruitPage" >/{1}.jsp</result>表达式也可以写在这里 -->
        </action>
    </package>
</struts>
复制代码

 

2.默认Action:
  在浏览器输入一个不存在的Action,页面将呈现404错误,为了网站更友好,我们可以设置一个默认的Action。
  
  注意:有一部份的朋友在某个自定义的action中定义default-action-ref这个配置的时候,认为在地址栏中输入地址如http://localhost:8080/project的时候(project为项目名),如果该项目后面不输入任何名字或者输错地址,则会自动进入default-action-ref定义的action并进入对应的类方法中进行操作并根据result返回页面,但是很多人发现结果并不是这样,而不管怎样都返回进入到index.jsp页面。

     实际上这一点从原理上来讲可以理解,default-action-ref这个配置的意思是当用户在点击了没有定义的action时,如果struts没有找到用户定义的action名称,则会自动跳转到该默认定义的action中。

    个人觉得地址栏中项目后不写名称和名称不存在是两个概念。

示列:

1. struts.xml 就在通配符例子中配置上默认Action

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" >
<struts>
    <package name="struts2" extends="struts-default">
    <!-- 配置默认Action -->
    <default-action-ref name="defaultAction"></default-action-ref>
        <action name="defaultAction">
            <result>/error.jsp</result>
        </action>
    
    <!-- 通配符映射(wildcard mappings) -->
        <action name="*Action" class="com.struts.ComputeAction" method="{1}" >
            <result name="fruitPage" >/fruit.jsp</result>
            <!-- <result name="fruitPage" >/{1}.jsp</result>表达式也可以写在这里 -->
        </action>
    </package>
</struts>
复制代码

2. index.jsp页面 这里我们把提交的url :Action地址链接,写错打断,当提交时找不到对应的Action,则会进入默认Action,进入error.jsp页面

复制代码
<body>

    <!-- 使用通配符映射(wildcard mappings)方式    使用:Struts2标签也可以使用HTML标签 -->
        <s:form name="form" theme="simple" >
        
            num1:<s:textfield name="num1" />
            num2:<s:textfield name="num2" />    <!-- 测试默认Action,当提交的Action地址错误。则会走默认Action -->
            <s:submit type="button" value="加" onclick="computeMethod('ssss')" /><!-- 把动态url地址乱写 -->
            <s:submit type="button" value="减" onclick="computeMethod('subtractActios')" /><!-- 或在url地址中多加字符 -->
        </s:form>
        
         
        
    <!-- js -->
    <script type="text/javascript">
        function computeMethod(op){
            document.form.action=op;//改动只有这里
            document.form.submit();//提交
        }
            
    </script>
            
  </body>
复制代码

3. error.jsp 创建此页面查看效果

  <body>
           错误页面。!
               未找到,Action实例时会默认走此页面!
  </body>

 

3.处理结果

  Struts2的Action处理完用户请求后,将返回一个普通字符串,整个普通字符串就是一个逻辑视图名。Struts2通过配置逻辑视图名和物理视图资源之间的映射关系,一旦系统收到Action返回的某个逻辑视图名,系统就会把对应的物理视图资源呈现给浏览者。

 
3.1    配置处理结果:
  Struts2的Action处理用户请求结束后,返回一个普通字符串-逻辑视图名,必须在struts.xml文件中完成逻辑视图和物理视图资源的映射,才可让系统转到实际的视图资源。
 
  Struts2通过在struts.xml文件中使用<result …/>元素来配置结果。Struts2提供了两种结果。
    局部结果:将<result …/>作为<action …>元素的子元素配置。
    全局结果:将<result …/>作为<global-results …>元素的子元素配置。
在package元素中配置<global-results>子元素:
    全局结果可满足一个包中多个Action共享一个结果:
    
<!-- 全局结果可满足一个包中多个Action共享一个结果,也就是说,当多个Action中都有一个重复的result时就可以使用全局结果,也就是说公共的result -->
    <global-results>
        <result name="fruitPage" type="dispatcher" >/fruit.jsp</result>
    </global-results>
 
3.2.    处理结果类型:
  Struts2提供了对不同种类返回结果的支持,常见的有JSP,FreeMarker,Velocity等。
  Struts2支持的不同类型的返回结果为:(加粗为常用)
 
名字 说明
chain 用来处理Action链
dispatcher 用来转向页面,通常处理JSP,这是默认的结果类型
freeMarker 处理FreeMarker模板
httpHeader 用来控制特殊的Http行为
redirect 重定向到一个URL
redirect-action 重定向到一个Action
stream 向浏览器发送InputSream对象,通常用来处理文件下载
velocity 处理Velocity模板
xslt 处理XML/XLST模板
plaintext 显示原始文件内容,例如文件源代码
tiles 结合Tile使用

  另外第三方的Result类型还包括JasperReports Plugin,专门用来处理JasperReport类型的报表输出;Jfreechart Plugin;JSF Plugin。

  常用示列:

1.struts.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" >
<struts>
    <package name="struts2" extends="struts-default">
    
        <!-- 默认Action -->
        <default-action-ref name="defaultAction"></default-action-ref>
        
        <!-- 全局结果可满足一个包中多个Action共享一个结果,也就是说,当多个Action中都有一个重复的result时就可以使用全局结果,也就是说公共的result
        <global-results>
            <result name="fruitPage" type="dispatcher" >/fruit.jsp</result>
        </global-results> -->
    
        <action name="defaultAction">
            <result>/error.jsp</result>
         </action>
        <!-- 通配符映射(wildcard mappings) -->
        <action name="*Action" class="com.struts.ComputeAction" method="{1}" >
            <!--1 
                表达式{1}也可以写在url连接中,class,name中都可以写,也可以写多少,索引从1开始
            <result name="fruitPage" >/{1}.jsp</result> -->
            
            <!--2
                默认dispatcher转发跳转
            <result name="fruitPage" type="dispatcher" >/fruit.jsp</result> -->
            
            <!--3
                重定向跳转
            <result name="fruitPage" type="redirect" >/fruit.jsp</result> -->
            
            <!--4
                redirectAction:    Action实例 与另一个Action实例互相跳转
            <result name="fruitPage" type="redirectAction" >skipAction</result> -->
            
            <!--4.1
                使用感叹号指定跳转方法,xml会显示报错,但可以用。    使用?&不能在Action实例中带参数
            <result name="fruitPage" type="redirectAction" >skipAction!add</result> -->
            
            <!--4.2
                跳转Action带参数的方式:
                    actionName:跳转Action的名称
                    method:跳转Action实例中的哪个方法
                    num:带的参数,可写固定,可使用${属性名}取上一个Action实例中的属性,实现动态传值。
             -->
            <result name="fruitPage" type="redirectAction" >
                <param name="actionName">skipAction</param>
                <param name="method">add</param>
                <param name="num1">${num1}</param>
                <param name="num2">${num2}</param>
            </result>
        </action>
        
        <action name="skipAction" class="com.struts.SkipAction" >
            <result name="success" type="dispatcher" >/fruit.jsp</result>
        </action>
    </package>
</struts>
复制代码

 

 2.再创建一个SkipAction 控制器的类

复制代码
package com.struts;
/**
 * 测试与dispatcherAction之间的传值
 * @author asus
 *
 */
public class SkipAction {

    /** 接收computeAction实例传过来属性 */
    private int num1;
    private int num2;
    private int result;//结果返回给页面
    
    public String execute(){
        System.out.println("当指定要走的方法时不会走此方法");
        return "success";
    }
    
    /**添加的方法 */
    public String add(){
        result=num1+num2;
        System.out.println("走add方法!");
        return "success";
    }

    /** Get,Set方法 */
    public int getNum1() {
        return num1;
    }

    public void setNum1(int num1) {
        this.num1 = num1;
    }

    public int getNum2() {
        return num2;
    }

    public void setNum2(int num2) {
        this.num2 = num2;
    }

    public int getResult() {
        return result;
    }

    public void setResult(int result) {
        this.result = result;
    }
    
}

 

 

 

 

 

复制代码
如何使用struts2拦截器,或者自定义拦截器。特别注意,在使用拦截器的时候,在Action里面必须最后一定要引用struts2自带的拦截器缺省堆栈defaultStack,如下(这里我是引用了struts2自带的checkbox拦截器):
<interceptor-ref name="checkbox">
  <param name="uncheckedValue">0</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>(必须加,否则出错)
也可以改为对全局Action设置自己需要的拦截器,如下:

在struts.xml里面定义全局的配置设置
  <package name="struts-shop" extends="struts-default">
    <interceptors>
      <interceptor-stack name="myStack">
        <interceptor-ref name="checkbox">
          <param name="uncheckedValue">0</param>
       </interceptor-ref>
       <interceptor-ref name="defaultStack"/>
      </interceptor-stack>
    </interceptors>
    <default-interceptor-ref name="myStack"/>(这句是设置所有Action自动调用的拦截器堆栈)
  </package>
 
struts-action.xml里面配置Action如下:
  <package name="LogonAdmin" extends="struts-shop">(这里扩展struts.xml里面定义的配置就可以了)
  <action name="logon" class="logonAction">
     <result>/jsp/smeishop/admin/index.jsp</result>
     <result name="error">/jsp/smeishop/admin/logon.jsp</result>
     <result name="input">/jsp/smeishop/admin/logon.jsp</result>
   </action>
   <action name="logout" class="logoutAction">
     <result>/jsp/smeishop/admin/logon.jsp</result>
   </action>
 </package>

你的拦截器可以正常工作了!!HOHO
以下是参考资料
struts2自带的配置及其拦截器配置
复制代码

Struts2 拦截器 [Interceptor]

拦截器的工作原理如上图,每一个Action请求都包装在一系列的拦截器的内部。拦截器可以在Action执行直线做相似的操作也可以在Action执行直后做回收操作。

 

每一个Action既可以将操作转交给下面的拦截器,Action也可以直接退出操作返回客户既定的画面。

 

如何自定义一个拦截器?

自定义一个拦截器需要三步:

1 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。

2 在strutx.xml中注册上一步中定义的拦截器。

3 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。

 

Interceptor接口声明了三个方法:

复制代码
public interface Interceptor extends Serializable {
 
    void destroy();
 
    void init();
 
    String intercept(ActionInvocation invocation) throws Exception;
}
复制代码

Init方法在拦截器类被创建之后,在对Action镜像拦截之前调用,相当于一个post-constructor方法,使用这个方法可以给拦截器类做必要的初始话操作。 

Destroy方法在拦截器被垃圾回收之前调用,用来回收init方法初始化的资源。 

Intercept是拦截器的主要拦截方法,如果需要调用后续的Action或者拦截器,只需要在该方法中调用invocation.invoke()方法即可,在该方法调用的前后可以插入Action调用前后拦截器需要做的方法。如果不需要调用后续的方法,则返回一个String类型的对象即可,例如Action.SUCCESS。

另外AbstractInterceptor提供了一个简单的Interceptor的实现,这个实现为

复制代码
public abstract class AbstractInterceptor implements Interceptor {
 
     public void init() {
    }
   
    public void destroy() {
    }
 
 
    public abstract String intercept(ActionInvocation invocation) throws Exception;
}
复制代码

在不需要编写init和destroy方法的时候,只需要从AbstractInterceptor继承而来,实现intercept方法即可。

 

我们尝试编写一个Session过滤用的拦截器,该拦截器查看用户Session中是否存在特定的属性(LOGIN属性)如果不存在,中止后续操作定位到LOGIN,否则执行原定操作,代码为:

复制代码
public class CheckLoginInterceptor extends AbstractInterceptor {
    public static final String LOGIN_KEY = "LOGIN";
    public static final String LOGIN_PAGE = "global.login";
 
    public String intercept(ActionInvocation actionInvocation) throws Exception {
 
        System.out.println("begin check login interceptor!");
        // 对LoginAction不做该项拦截
        Object action = actionInvocation.getAction();
        if (action instanceof LoginAction) {
            System.out.println("exit check login, because this is login action.");
            return actionInvocation.invoke();
        }
 
        // 确认Session中是否存在LOGIN
        Map session = actionInvocation.getInvocationContext().getSession();
        String login = (String) session.get(LOGIN_KEY);
        if (login != null && login.length() > 0) {
            // 存在的情况下进行后续操作。
            System.out.println("already login!");
            return actionInvocation.invoke();
        } else {
            // 否则终止后续操作,返回LOGIN
            System.out.println("no login, forward login page!");
            return LOGIN_PAGE;
        }
    }
}
 
注册拦截器
<interceptors>
            <interceptor
name="login" 
class="com.jpleasure.teamware.util.CheckLoginInterceptor"/>
            <interceptor-stack name="teamwareStack">
                <interceptor-ref name="login"/>
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>
</interceptors>
 
将上述拦截器设定为默认拦截器:
<default-interceptor-ref name="teamwareStack"/>
这样在后续同一个package内部的所有Action执行之前都会被login拦截。
复制代码

Struts2(XWork)提供的拦截器的功能说明:

拦截器

名字

说明

Alias Interceptor

alias

在不同请求之间将请求参数在不同名字件转换,请求内容不变

Chaining Interceptor

chain

让前一个Action的属性可以被后一个Action访问,现在和chain类型的result(<result type=”chain”>)结合使用。

Checkbox Interceptor

checkbox

添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox。

Cookies Interceptor

cookies

使用配置的name,value来是指cookies

Conversion Error Interceptor

conversionError

将错误从ActionContext中添加到Action的属性字段中。

Create Session Interceptor

createSession

自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。

Debugging Interceptor

debugging

提供不同的调试用的页面来展现内部的数据状况。

Execute and Wait Interceptor

execAndWait

在后台执行Action,同时将用户带到一个中间的等待页面。

Exception Interceptor

exception

将异常定位到一个画面

File Upload Interceptor

fileUpload

提供文件上传功能

I18n Interceptor

i18n

记录用户选择的locale

Logger Interceptor

logger

输出Action的名字

Message Store Interceptor

store

存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。

Model Driven Interceptor

model-driven

如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。

Scoped Model Driven

scoped-model-driven

如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用Action的setModel方法将其放入Action内部。

Parameters Interceptor

params

将请求中的参数设置到Action中去。

Prepare Interceptor

prepare

如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。

Scope Interceptor

scope

将Action状态存入session和application的简单方法。

Servlet Config Interceptor

servletConfig

提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。

Static Parameters Interceptor

staticParams

从struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中。

Roles Interceptor

roles

确定用户是否具有JAAS指定的Role,否则不予执行。

Timer Interceptor

timer

输出Action执行的时间

Token Interceptor

token

通过Token来避免双击

Token Session Interceptor

tokenSession

和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中

Validation Interceptor

validation

使用action-validation.xml文件中定义的内容校验提交的数据。

Workflow Interceptor

workflow

调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面

Parameter Filter Interceptor

N/A

从参数列表中删除不必要的参数

Profiling Interceptor

profiling

通过参数激活profile

复制代码
注册并引用Interceptor
<package name="default" extends="struts-default">
   <interceptors>
       <interceptor name="timer" class=".."/>
       <interceptor name="logger" class=".."/>
   </interceptors>
 
   <action name="login" class="tutorial.Login">
        <interceptor-ref name="timer"/>
        <interceptor-ref name="logger"/>
        <result name="input">login.jsp</result>
        <result name="success"
            type="redirect-action">/secure/home</result>
   </action>
</package>
 
可以将多个拦截器合并在一起作为一个堆栈调用,当一个拦截器堆栈被附加到一个Action的时候,要想Action执行,必须执行拦截器堆栈中的每一个拦截器。
<package name="default" extends="struts-default">
   <interceptors>
        <interceptor name="timer" class=".."/>
        <interceptor name="logger" class=".."/>
        <interceptor-stack name="myStack">
           <interceptor-ref name="timer"/>
           <interceptor-ref name="logger"/>
        </interceptor-stack>
    </interceptors>
 
    <action name="login" class="tutuorial.Login">
         <interceptor-ref name="myStack"/>
         <result name="input">login.jsp</result>
         <result name="success"
             type="redirect-action">/secure/home</result>
    </action>
</package>
 
上述说明的拦截器在默认的Struts2应用中,根据惯例配置了若干个拦截器堆栈,详细情参看struts-default.xml
其中有一个拦截器堆栈比较特殊,他会应用在默认的每一个Action上。
<interceptor-stack name="defaultStack">
    <interceptor-ref name="exception"/>
    <interceptor-ref name="alias"/>
    <interceptor-ref name="servletConfig"/>
    <interceptor-ref name="prepare"/>
    <interceptor-ref name="i18n"/>
    <interceptor-ref name="chain"/>
    <interceptor-ref name="debugging"/>
    <interceptor-ref name="profiling"/>
    <interceptor-ref name="scopedModelDriven"/>
    <interceptor-ref name="modelDriven"/>
    <interceptor-ref name="fileUpload"/>
    <interceptor-ref name="checkbox"/>
    <interceptor-ref name="staticParams"/>
    <interceptor-ref name="params">
      <param name="excludeParams">dojo"..*</param>
    </interceptor-ref>
    <interceptor-ref name="conversionError"/>
    <interceptor-ref name="validation">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
    <interceptor-ref name="workflow">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
</interceptor-stack>
 
每一个拦截器都可以配置参数,有两种方式配置参数,一是针对每一个拦截器定义参数,二是针对一个拦截器堆栈统一定义所有的参数,例如:
<interceptor-ref name="validation">
 <param name="excludeMethods">myValidationExcudeMethod</param>
</interceptor-ref>
<interceptor-ref name="workflow">
 <param name="excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
或者
<interceptor-ref name="defaultStack">
    <param name="validation.excludeMethods">myValidationExcludeMethod</param>
    <param name="workflow.excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
 
每一个拦截器都有两个默认的参数:
excludeMethods - 过滤掉不使用拦截器的方法和
includeMethods – 使用拦截器的方法。
 
需要说明的几点:
1 拦截器执行的顺序按照定义的顺序执行,例如:
<interceptor-stack name="xaStack">
 <interceptor-ref name="thisWillRunFirstInterceptor"/>
 <interceptor-ref name="thisWillRunNextInterceptor"/>
 <interceptor-ref name="followedByThisInterceptor"/>
 <interceptor-ref name="thisWillRunLastInterceptor"/>
</interceptor-stack>
的执行顺序为:
thisWillRunFirstInterceptor
 thisWillRunNextInterceptor
    followedByThisInterceptor
      thisWillRunLastInterceptor
        MyAction1
        MyAction2 (chain)
        MyPreResultListener
        MyResult (result)
      thisWillRunLastInterceptor
    followedByThisInterceptor
 thisWillRunNextInterceptor
thisWillRunFirstInterceptor
 
2 使用默认拦截器配置每个Action都需要的拦截器堆栈,例如:
<action name="login" class="tutorial.Login">
     <interceptor-ref name="timer"/>
     <interceptor-ref name="logger"/>
     <interceptor-ref name="default-stack"/>
 
     <result name="input">login.jsp</result>
     <result type="redirect-action">/secure/home</result>
</action>
可以按照如下的方式定义:
<interceptors>
     <interceptor-stack name="myStack">
        <interceptor-ref name="timer"/>
        <interceptor-ref name="logger"/>
        <interceptor-ref name="default-stack"/>
     </interceptor-stack>
</interceptors>
 
<default-interceptor-ref name="myStack"/>
 
<action name="login" class="tutorial.Login">
       <result name="input">login.jsp</result>
       <result type="redirect-action">/secure/home</result>
</action>
 
3 如何访问HttpServletRequest,HttpServletResponse或者HttpSession
有两种方法可以达到效果,使用ActionContext:
Map attibutes = ActionContext.getContext().getSession();
或者实现相应的接口:
HttpSession            SessionAware
HttpServletRequest     ServletRequestAware
HttpServletResponse    ServletResponseAware


Struts2自定义拦截器实例—只允许从登录页面进入系统
【1】struts.xml:
 <!-- 定义一个拦截器 -->  
        <interceptors>  
            <interceptor name="authority"  
                class="org.interceptot.LoginInterceptor">  
            </interceptor>  
            
            <!-- 拦截器栈 -->  
            <interceptor-stack name="mydefault">  
                <interceptor-ref name="defaultStack" />  
                <interceptor-ref name="authority" />  
            </interceptor-stack>  
        </interceptors>
        
         
        
         <!-- 定义全局Result -->  
        <global-results>  
            <!-- 当返回login视图名时,转入/login.jsp页面 -->  
            <result name="login">/login.jsp</result>  
        </global-results>
        
        
        <action name="show" class="org.action.showAction">  
            <result name="success">/main.jsp</result>  
            <!-- 使用此拦截器 -->  
            <interceptor-ref name="mydefault" />  
        </action>  
        
        
        <!--验证登录用户信息  -->
        <action name="login" class="org.action.loginAction" method="execute">
            <result name="error">/login.jsp</result>  
            <result name="input">/login.jsp</result> 
        </action>
       
【2】自定义拦截器org.interceptot.LoginInterceptor:
package org.interceptot;
import java.util.Map;  

import com.opensymphony.xwork2.Action;  
import com.opensymphony.xwork2.ActionContext;  
import com.opensymphony.xwork2.ActionInvocation;  
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;  
public class LoginInterceptor extends AbstractInterceptor {  
 
    @Override  
    public String intercept(ActionInvocation invocation) throws Exception {  
  
        // 取得请求相关的ActionContext实例  
        ActionContext ctx = invocation.getInvocationContext();  
        Map session = ctx.getSession();  
        String user = (String) session.get("username");  
  
        // 如果没有登陆,即用户名不存在,都返回重新登陆  
        System.out.println("user:"+user);
        if (user != null) {  
            System.out.println("test");  
            return invocation.invoke();  
        }  
        System.out.println("你还没有登录"); 
        ctx.put("tip", "你还没有登录");  
        return Action.LOGIN;    //返回一个叫login的result结果
  
    }  
  
}  
【3】进入主页面的Action:org.action.showAction
package org.action;

import com.opensymphony.xwork2.ActionSupport;  

public class showAction extends ActionSupport {  
 public String execute() {  
  return "success";  
 }  
}  

【4】LoginAction:

private boolean isInvalid(String value) {     
return (value == null || value.length() == 0);     
}  
if (isInvalid(user.getUsername()))   
       return INPUT;     
 if (isInvalid(user.getPassword()))     
       return INPUT;    
 //登录成功将User放入session中
HttpServletRequest request = ServletActionContext.getRequest();
Map  map=ActionContext.getContext().getSession();
map.put("username", user.getUsername());

【5】如果我们通过show.action访问main.jsp那么就会被自定义拦截器拦住,拦截器检查session中
是否有值,有证明用户已经登录,没有则为没有登录,那么就会被跳转到登陆页面。

 

 
posted @ 2022-02-14 21:36  hanease  阅读(712)  评论(0编辑  收藏  举报