Struts2 拦截器详解(一)

来源:互联网 发布:单片机步进电机c程序 编辑:程序博客网 时间:2024/05/01 22:17

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

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

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

Struts2的拦截原理:

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

1.配置拦截器

引入拦截器机制,可以实现对Action通用操作的可插拔式管理,这种可插拨是基于Struts.xml文件实现的。

1)配置拦截器

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

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

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

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


2)配置拦截器栈

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

拦截器栈的定义形式:

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


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

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

<interceptor name="a1" class="com.zhuxuli.SInterceptor">   </interceptor>   <interceptor name="a2" class="com.zhuxuli.DInterceptor">   </interceptor>   <interceptor name="a3" class="com.zhuxuli.WInterceptor"></interceptor>      <interceptor-stack name="a4">      <interceptor-ref name="a1">         <param name="st">gsa</param>      </interceptor-ref>      <interceptor-ref name="a2">      </interceptor-ref>   </interceptor-stack>


2.使用拦截器

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

示例代码如下:

<action name="user" class="com.zhuxuli.action.UserAction">          <result name="user">/user.jsp</result>          <result name="add_user">/add_input.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>

3.拦截器剖析

拦截器的配置已经说的很明白了,但是我们还一点疑问是,那个拦截器的实现类怎么办?

对于Struts2拦截器的实现有两种形式

第一种方式:自定义拦截器类

第二种方式:Struts2自带的拦截器

首先我们说明下第一种方式:

上述代码

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


我们可以看到,自定义拦截器只有name=“拦截器名”是不可以的,我们还要有拦截器类,那拦截器类是怎么实现的?

1)实现自定义拦截器类

如果要开发自己的拦截器类,应该实现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;}}


 

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.zhuxuli.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页面的时候,你会看到控制台打印出了相应的语句,也就是下面的代码执行了

                       Stringresult=invocation.invoke();

                      

                       //代码执行了

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

                       returnresult;

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

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

原创粉丝点击