struct2拦截器

来源:互联网 发布:通联支付网络给我打钱 编辑:程序博客网 时间:2024/05/16 09:30

         拦截器是Struts2的一个重要特性。Struts2框架的大多数核心功能都是通过拦截器来实现的,像避免表单重复提交、类型转换、对象组装、验证、文件上传等,都是在拦截器的帮助下实现的。

         拦截器的一个重要特征是:它可以在Action之前调用。

         拦截器,在AOP(Aspect Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截。然后再之前或之后加入某些操作。拦截器是AOP的一种实现策略。

Struts2的拦截原理:

        当请求到达Struts2的ServletDispatcher时,Struts2会查找相对应的配置信息,并实例化拦截器对象。串成一个列表(list),最后一个一个地调用列表中的拦截器。

  • 配置拦截器

在struts.xml文件中定义拦截器只需为拦截器指定一个拦截器名称即可。使用<interceptor.../>元素来定义拦截器,最简单的格式如下。

<interceptor name="拦截器名" class="拦截器类"></interceptor>  

有时,按照上面的配置便可以完成一个简单的拦截器,但是,如果还需要配置拦截器时传入拦截器参数,则需要在<interceptor.../>元素中使用<param.../>子元素。下面是在配置拦截器时,同时传入拦截器参数的配置形式。

<interceptor name="拦截器名" class="拦截器类">     <!-- 下面元素可以出现0次,也可以出现无数次,其中name属性指定需要设置的参数名 -->     <param name="参数名">参数值</param>  </interceptor>  


  • 配置拦截器栈

        struts2还支持把多个拦截器连在一起组成一个拦截器栈,比如:需要在Action执行前同时进行安全检查,身份验证,数据校验等等操作,可以将这些动作链接成一个拦截器栈。

拦截器栈的定义形式:


<interceptor-stack name="拦截器栈名">        <interceptor-ref name="拦截器一"></interceptor-ref>        <interceptor-ref name="拦截器二"></interceptor-ref>  </interceptor-stack>

拦截器和拦截器栈的功能是一样的,只不过拦截器栈定义了一组拦截器。

当然我们也可以在拦截器中定义拦截器栈,这样都是可以的,主要是实现了代码的复用。比如:


<interceptor name="a1" class="com.yao.SInterceptor">  </interceptor>  <interceptor name="a2" class="com.yao.DInterceptor">  </interceptor>  <interceptor name="a3" class="com.yao.Interceptor"></interceptor>          <interceptor-stack name="a4">        <interceptor-ref name="a1">           <param name="st">abc</param>        </interceptor-ref>        <interceptor-ref name="a2">        </interceptor-ref>  </interceptor-stack>

  • 使用拦截器

拦截器在<package>目录下声明好了以后,下一步就是在Action中使用拦截器了

示例代码如下:

<action name="user" class="com.yao.action.UserAction">            <result name="user">/user.jsp</result>            <result name="add_user">/add_user.jsp</result>            <interceptor-ref name="myfilter">                <param name="name">拦截器</param>                <!-- 指定execute方法不需要被拦截 -->                <param name="excludeMethods">execute</param>            </interceptor-ref>   </action>  

通过<interceptor标签直接引用拦截器。

如果是使用拦截器栈,那示例代码如下:

<action name="ge">         <result name="success">/success.jsp</result>         <interceptor-ref name="a1"></interceptor-ref>         <!-- 定义使用的拦截器栈,和使用拦截器没有什么区别,都是通过interceptor-ref标签完成 -->         <interceptor-ref name="a4"></interceptor-ref>  </action> 



  • 拦截器剖析
    • 实现自定义拦截器类
如果要开发自己的拦截器类,应该实现com.opensymphony.xwork2.interceptor.Interceptor接口,此接口的定义代码如下:


import com.opensymphony.xwork2.ActionInvocation;    public interface Interceptor {      //撤销该拦截器之前的回调方法      void destory();      //初始化该拦截器的回调方法      void init();      //拦截器实现拦截的逻辑方法      String intercept(ActionInvocation invocation) throws Exception;  } 


通过接口可以看出,该接口里有三种方法。

        init():拦截器初始化之后,在该拦截器执行拦截之前,系统将回调该方法,对于每一个拦截器而言,该init()方法只执行一次。因此,该方法的方法体主要用户打开一些一次性资源,例如数据库资源等。

        destory():该方法与init()方法对应。在拦截实例被销毁之前,系统将回调该拦截器的destory()方法,该方法用于销毁在init()方法里打开的资源。

        interceptor(ActionInvocation invocation):该方法是用户需要实现的拦截动作。就像Action执行execute()方法一样,interceptor会返回一个字符串作为逻辑视图。如果该方法直接返回了一个字符串,系统将跳转到该逻辑视图队形的实际的视图资源,不会调用被拦截的Action类。该方法的ActionInvocation参数保护那了被拦截的Action的引用,可以通过调用该参数的invoke()方法,将控制权转给下一个拦截器,或者转给Action的execute方法。

         除此之外,Struts2提供了一个AbstractInterceptor类,该类提供了空的init()和destory()方法的实现,也就是如果拦截器不需要申请资源,则可以无须实现这两个方法。

注意:

         当实现了intercept(ActionInvocation invocation)方法时,可以获得ActionInvocation参赛,这个参数又可以获得被拦截的Action实例,一旦取得了Action实例,几乎得到了全部的控制权。


实例剖析:

通过上面的了解,我们来做一个拦截器的实例

需求:客户端用户名密码登陆,拦截器对其进行拦截,如果用户名和密码满足条件,进入执行execute方法,如果不满足要求,返回index.jsp页面

建立相应的index.jsp页面


<%@ page language="java" import="java.util.*" pageEncoding="GB18030"%>  <%@ taglib prefix="s" uri="/struts-tags" %>  <%  String path = request.getContextPath();  String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  %>    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  <html>    <head>      <base href="<%=basePath%>">            <title>My JSP 'index.jsp' starting page</title>      <meta http-equiv="pragma" content="no-cache">      <meta http-equiv="cache-control" content="no-cache">      <meta http-equiv="expires" content="0">          <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">      <meta http-equiv="description" content="This is my page">      <!--     <link rel="stylesheet" type="text/css" href="styles.css">     -->    </head>        <body>       <s:form action="los" method="post">          <s:textfield label="请输入用户名" name="username"></s:textfield>          <s:password label="请输入密码" name="userpass"></s:password>          <s:submit/>       </s:form>    </body>  </html>  

定义Action类

Action中定义了接受前台两个参数的属性


import com.opensymphony.xwork2.ActionSupport;    public class LoginAction extends ActionSupport {      private String username;      private String userpass;      public String getUsername() {          return username;      }      public void setUsername(String username) {          this.username = username;      }      public String getUserpass() {          return userpass;      }      public void setUserpass(String userpass) {          this.userpass = userpass;      }      public String execute() throws Exception{          return SUCCESS;      }  }  


         该拦截器类继承了AbstractInterceptor类,可以不写init()和destory()方法,但是必须实现intercept(ActionInvocation invocation)方法

当拦截到内容后,系统首先打印出"拦截器开始工作......"

由于invocation中已经存在Action对象,所以按照我们说的得Action者得天下原则,为Action中的两个属性username ,userpass赋值。


        这里存在一个很重要的问题:如果不为Action的两个元素赋值,那Action类不像我们以前没有拦截器认识的一样,Action中的username和userpass将不会在自动取得前台index.jsp页面传送过来的值,即使满足条件,如果Action类中还需要对username和userpass两个属性做进一步处理的话,username和userpass将会是空值。


解决办法:

        首先必须明白的是在提交后,Struts2将会自动调用LoginAction动作类中的setUsername方法,并将username文本框中的值通过setUserame方法的参数传入。实际上,这个操作是由params拦截器完成的,params对应的类是com.opensymphony.xwork2.interceptor.ParametersInterceptor。由于params已经在defaultStack中定义,因此,在未引用拦截器的< action>中是会自动引用params的。

但是,如果引用了自定义拦截器,那我们就要现实的在Action中引用params拦截器。


如果满足用户名和密码是user 和123,则调用invocation.invoke()方法,该方法的作用是调用下一个拦截器或Action(如果是最后一个拦截器了,则调用Action);


import javax.servlet.http.HttpServletRequest;    import org.apache.struts2.ServletActionContext;    import com.opensymphony.xwork2.ActionInvocation;  import com.opensymphony.xwork2.interceptor.AbstractInterceptor;  import com.zhuxuli.action.LoginAction;    public class LoInterceptor extends AbstractInterceptor{      public String intercept(ActionInvocation invocation) throws Exception{          System.out.println("拦截器开始工作.....");          HttpServletRequest request=ServletActionContext.getRequest();          LoginAction action=(LoginAction)invocation.getAction();          action.setUsername(request.getParameter("username"));          action.setUserpass(request.getParameter("userpass"));          if(action.getUsername().equals("user")&&action.getUserpass().equals("123")){              String result=invocation.invoke();              System.out.println("result="+result);              return result;          }else{              return "input";          }      }  }  

然后配置struts.xml文件


<?xml version="1.0" encoding="UTF-8"?>  <!DOCTYPE struts PUBLIC      "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"      "http://struts.apache.org/dtds/struts-2.1.7.dtd">  <struts>     <constant name="struts.i18n.encoding" value="gbk" />      <constant name="struts.devMode" value="true" />     <package name="zxl" extends="struts-default">         <interceptors>            <interceptor name="a1" class="com.zhuxuli.Iterceptors.LoInterceptor">            </interceptor>         </interceptors>         <action name="los" class="com.yao.action.LoginAction">            <result name="input">/index.jsp</result>            <result name="success">/success.jsp</result>            <interceptor-ref name="a1"></interceptor-ref>         </action>     </package>  </struts>  

实例运行后,如果用户名和密码不是user和123,页面返回index.jsp页面继续输入,满足条件,则返回为success.jsp页面。


必须注意的是:

在执行返回success.jsp页面的时候,你会看到控制台打印出了相应的语句,也就是下面的代码执行了


System.out.println("result="+result);return  result;

        也就是说,通过invocation调用invoke()方法执行Action后,Action执行完毕后,又返回到String result=invocation.invoke()代码下面继续执行,直到执行结束,也就说明了拦截器的拦截过程为:如下图。


        说明:首先调用拦截器1,满足要求,则通过Invocation.invoke()方法调用下一个拦截器或Action,因为这里有下一个拦截器,所以直接调用拦截器2,如果还是满足条件,则继续向下调用,知道返回结果,返回结果后,注意的是,还会继续回到拦截器2执行未完成的代码,执行完后,继续执行拦截器1未完成的代码,直到最后返回结果。







  • Struct2常用的预定义拦截器

1:params拦截器

这个拦截器是必不可少的,因为就是由它把请求参数设置到相应的Action的属性去的,并自动进行类型转换。


2:staticParams拦截器

       将struts.xml配置文件里定义的Action参数,设置到对应的Action实例中,Action参数使用<param>标签,是<action>标签的子元素。


struts.xml的示例如下:

<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">                <param name="account">test</param>    </action>


        这要求Action中一定要有一个account的属性,并有相应的getter/setter方法。运行的时候,Action的account属性在初始化过后,会接到这里的赋值“test”。

       注意:params拦截器和staticParams拦截器都会为Action的属性赋值,如果碰到了都要赋同一个值呢,比如request里面有account参数,而struts.xml中也有account参数,最终的值是谁?

       其实是Action初始化过后,就会把struts.xml中配置的数据设置到Action实例中相应的属性上去。然后,把用户请求的数据设置到Action实例中相应的属性上去。

       很明显最后的值是用户请求中account的数据。


3:prepare拦截器

        在Action执行之前调用Action的prepare()方法,这个方法是用来准备Action执行之前要做的工作。它要求我们的Action必需实现com.opensymphony.xwork2.Preparable接口


4:modelDriven拦截器

       如果Action实现ModelDriven接口,它将getModel()取得的模型对象存入OgnlValueStack中。


5:chain拦截器

       将前一个执行结束的Action属性设置到当前的Action中。它被用在ResultType为“chain”所指定的结果的Action中,该结果Action对象会从值栈中获得前一个Action对应的属性,它实现Action链之间的数据传递。


6:execption拦截器

       在抛出异常的时候,这个拦截器起作用。它是我们第五章讲的Struts2的错误处理机制(<exception-mapping>)的基础,任何应用都应该引用这个拦截器,而且引用的时候,最好把它放在第一位,让它能捕获所有的异常。


7:validation拦截器

      调用验证框架读取 *-validation.xml文件,并且应用在这些文件中声明的校验。


8:token拦截器

      核对当前Action请求(request)的有效标识,防止重复提交Action请求 。使用标签<s:token>可以生成表单令牌,该标签会在session中设置一个预期的值并且在表单中创建一个隐藏的input字段。Token拦截器会检查这个令牌,如果不合法,将不会执行action,注意这个拦截器需要手工添加,还需要配置一个invalid.token的result。


9:tokenSession拦截器

      扩展了token拦截器的功能,当提交无效的Action请求标识时,它会跳转回到第一次成功后的页面。


10:conversionError拦截器

       用来处理框架进行类型转化(Type Conversion)时的出错信息。它将存储在ActionContext中的类型转化(Type Conversion)错误信息转化成相应的Action字段的错误信息,保存在堆栈中。根据需要,可以将这些错误信息在视图中显示出来。


11:fileUpload拦截器

      用来处理文件上传。


12:workflow拦截器

        Action默认的工作流,如果action实现了Validateable接口,那么interceptor会调用action的validate()方法;如果action实现了ValidationAware接口,那么interceptor将会检查action是否包含错误信息。如果包含任何错误信息,那么interceptor将会返回input,而不让action执行。


13:servletConfig拦截器

        这个拦截器提供Action直接对Servlet API的访问,把Servlet API的对象注入到Action中。包括:ServletRequestAware、ServletResponseAware、ParameterAware、SessionAware、ApplicationAware。


14:timer拦截器

       记录ActionInvocation余下部分执行的时间,并做为日志信息记录下来,便于寻找性能瓶颈。


15:logger拦截器

       在日志信息中输出要执行的Action信息 ,这样,在调试的时候,就能很快的定位到这个对应的Action了。












原创粉丝点击