Struts2笔记 - 03 拦截器

Posted 完齿猪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2笔记 - 03 拦截器相关的知识,希望对你有一定的参考价值。


• 拦截器

    拦截器(Interceptor)是 Struts2 框架的核心组成部分,它类似于 Servlet 中的过滤器,是一种可以在请求之前或之后执行的 Struts2 的组件,也可以将其理解为动态拦截 Action 调用的对象。

     在 Struts2 框架中,拦截器是其重要的组成部分,Struts2 的很多功能(数据校验、对象类型转换、文件上传等)都是构建在拦截器之上的。

<package name="default" namespace="/" extends="struts-default">
    <!--声明拦截器-->
    <interceptors>
        <interceptor name="interceptor1" class="interceptorClass"/>
        <interceptor name="interceptor2" class="interceptorClass"/>
        <!--定义一个拦截器栈myStack,该拦截器栈中包含两个拦截器和一个默认拦截器栈-->
        <interceptor-stack name="myStack">
            <interceptor-ref name="defaultStack"/>
            <interceptor-ref name="interceptor1"/>
            <interceptor-ref name="interceptor2"/>
        </interceptor-stack>
    </interceptors>
</package>

    在早期的 MVC 框架中,通常会将一些通用的操作(如类型转换、数据校验、解析上传的文件等)强制写在控制器中,而这些常用操作又不是所有的请求都需要实现的,这就导致了框架的灵活性不足、可扩展性低等问题。

    通常情况下,开发者通过 Struts2 内建的拦截器可以完成大部分的操作,只有在内建拦截器不能满足需求时,才会自己扩展。可以这么说,Struts2 框架的简单易用,与拦截器的作用是分不开的。

在 Struts2 框架中,这些通用的核心功能都放到了拦截器中实现,而不是集中放在核心控制器中实现。



• 拦截器栈

    当多个拦截器组合在一起时就形成了拦截器链(Interceptor Chain)或拦截器栈(Interceptor Stack)。

    拦截器链就是指对应各个功能的拦截器按照一定的顺序排列在一起形成的链,而拦截器链组成的集合就是拦截器栈。当有适配连接器栈的访问请求进来时,这些拦截器就会按照之前定义的顺序被调用。

    在通常情况下,拦截器都是以代理方式调用的,它在一个 Action 执行前后进行拦截,围绕着 Action 和 Result 的执行而执行。

    · 从结构上看,拦截器栈相当于多个拦截器的组合。

    · 在功能上看,拦截器栈也是拦截器。

    · 拦截器的执行过程是一个递归的过程,工作原理类似于过滤器。



• 内置拦截器

    Struts2 框架中内置了许多拦截器,这些拦截器以 name-class 对的形式配置在 struts-default.xml 文件中。可在struts核心包的struts-default.xml中查看

    · name 是拦截器的名称,也就是引用的名字;

    · class 指定了该拦截器所对应的实现。

Struts2笔记 - 03 拦截器

  ▪ alias

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

  ▪ autowiring

    用于实现 Action 的自动装配

  ▪ chain

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

  ▪ conversionError

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

  ▪ cookies

    使用配置的 Name 和 Value 指定 Cookies

  ▪ cookieProvider

    该类是一个 Cookie 工具,方便开发者向客户端写 Cookie

  ▪ clearSession

    用于清除一个 HttpSession 实例

  ▪ createSession

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

  ▪ debugging

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

  ▪ execAndWait

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

  ▪ exception

    将异常定位到一个画面

  ▪ fileUpload

    提供文件上传功能

  ▪ il8n

    记录用户选择的 locale

  ▪ logger

    输出 Action 的名称

  ▪ model-driven

    如果一个类实现了 Model Driven,将 get Model 得到的结果放在 Value Slack 中

  ▪ scoped-model-driven

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

  ▪ params

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

  ▪ actionMappingParams

    用于负责在 Action 配置中传递参数

  ▪ prepare

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

  ▪ staticParams

    将 struts.xml 文件中 <action>标签的参数内容设置到对应的 Action 中

  ▪ scope

    将 Action 状态存入 session 和 application 范围

  ▪ servletConfig

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

  ▪ timer

    输出 Action 执行的时间

  ▪ token

    通过 Token 避免双击

  ▪ tokenSession

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

  ▪ validation

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

  ▪ workflow

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

  ▪ store

    存储或者访问实现 ValidalionAware 接口的 Action 类出现的消息、错误和字段错误等

  ▪ checkbox

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

  ▪ datetime

    日期拦截器

  ▪ profiling

    通过参数激活 profile

  ▪ roles

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

  ▪ annotationWorkflow

    利用注解代替 XML 配置,使用 annotationWorkflow 拦截器可以使用注解,执行流程为 before-execute-feforeResult-after

  ▪ multiselect

    检测是否有像 <select> 标签一样被选中的多个值,然后添加一个空参数

  ▪ deprecation

    当日志级别设置为调试模式(debug)并且没有特殊参数时,在 devMode 模式中,会检查应用程序使用过时或未知的常量,并且显示警告。



    • 默认拦截器栈

    如果想对一个包下的 Action 使用相同的拦截器,则需要为该包(package)中的每个 Action 都重复指定同一个拦截器,这样写显然过于繁琐。

    为了解决此问题,Struts2 中支持使用默认拦截器,它可以对其指定的包中的所有 Action 都起到拦截作用。

    配置默认拦截器需要使用 <default-interceptor-ref> 元素,此元素为 <package> 元素的子元素。

    一旦为某一个包指定了默认拦截器,并且该包中的 Action 未显示指定拦截器,则会使用默认拦截器。反之,若此包中的 Action 显示的指定了某个拦截器,则该默认拦截器将会被屏蔽。此时,如果还想使用默认拦截器,则需要用户手动配置该默认拦截器的引用。

<interceptors>
  <interceptor name="mytimer" class="com.ex02_Interceptor.interceptor.TimeInterceptor" />
  <interceptor name="auth" class="com.ex03_auth.AuthInterceptor"></interceptor>
  <!-- 自定义拦截器栈 -->
  <interceptor-stack name="myStack">
    <interceptor-ref name="defaultStack"></interceptor-ref>
    <interceptor-ref name="auth"></interceptor-ref>
  </interceptor-stack>
</interceptors>

    · 在Struts2的自带设定中,struts-default.xml中定义一个defaulStack拦截器栈并已经将其指定为默认拦截器。

    · 只要在定义包的过程中继承struts-default包,那么defaultStack将是默认的拦截器。

    · 当包中的某个action显式地指定了某个拦截器,则默认拦截器不会起作用。

    · 拦截器栈中的各个拦截器的顺序很重要。



    • 实现方法

    (1)方式一:实现Interceptor接口

     ▪ void init():初始化拦截器所需资源

     ▪ void destroy():释放在init()中分配的资源

     ▪ String intercept(ActionInvocation ai) throws Exception 

       · 实现拦截器功能

       · 利用ActionInvocation参数获取Action状态

       · 返回result字符串作为逻辑视图

    (2)方式二:继承AbstractInterceptor类

     ▪ 提供了init()和destroy()方法的空实现

     ▪ 只需要实现intercept方法即可

     因为实现Interceptor接口需要实现init()和destroy()方法,比较麻烦,在实际开发中一般使用第二种方式。



• 案例1 - 计算Action的执行时间

    · 创建拦截器类

    · 在配置文件中定义拦截器,并使用

(1)配置web.xml

    Struts2 的本质是一个过滤器:StrutsPrepareAndExecuteFilter。

<filter>
  <filter-name>struts2</filter-name>
  <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>struts2</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>


(2)创建TimeAction.java

import com.opensymphony.xwork2.ActionSupport;

public class TimeAction extends ActionSupport {
  private static final long serialVersionUID = 1L;

  @Override
  public String execute() throws Exception {

    for (int i = 0; i <= 10000; i++) {
      System.out.println("循环输出1万次:[" + i + "];");
    }
    return SUCCESS;
  }
}


(3)创建TimeInterceptor.java

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class TimeInterceptor extends AbstractInterceptor {
  private static final long serialVersionUID = 1L;

  @Override
  public String intercept(ActionInvocation invocation) throws Exception {
    // 执行action之前
    long starttime = System.currentTimeMillis();
    // 逐一执行拦截器,如果是最后一个拦截器则执行action,返回的结果就是action return的字符串
    String result = invocation.invoke();
    //执行action之后
    long endtime = System.currentTimeMillis();
    
    System.out.println("执行Action花费的时间:" + (endtime - starttime) + "ms");
    return result;
  }
}


(4)配置struts.xml

<struts>
  <package name="default" namespace="/" extends="struts-default">
    <interceptors>
      <interceptor name="mytimer" class="com.ex02_Interceptor.interceptor.TimeInterceptor" />
    </interceptors>
    <!--拦截器案例 -->
    <action name="timer" class="com.ex02_Interceptor.action.TimeAction">
      <result>/ex02/success.jsp</result>
      <!-- 为Action显示引用拦截器后,默认的拦截器defaultStack不再生效,需要手工引用 -->
      <interceptor-ref name="defaultStack"></interceptor-ref>
      <!-- 引用拦截器 -->
      <interceptor-ref name="mytimer"></interceptor-ref>
    </action>
  </package>
</struts>


(5)创建JSP

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>计算action花费时间</title>
</head>
<body>
 <a href="timer">计算被返回访问花费的时间</a>
</body>
</html>

(6)测试

Struts2笔记 - 03 拦截器

http://localhost:8088/Demo_Struts2/ex03/index.jsp

    · jsp上点击链接 <a href="timer"> 

    · 被web.xml定义的<filter-name>struts2截获,指向<action name="timer"。

    · 但在在访问action前,被拦截器<interceptor-ref name="mytimer">拦截了,执行TimeInterceptor.java,到String result = invocation.invoke();进入TimeAction.java

    · 执行完TimeAction.java返回TimeInterceptor.java。

    · 成功后,返回/ex02/success.jsp

Struts2笔记 - 03 拦截器



• 案例2 - 使用拦截器进行用户权限验证

Struts2笔记 - 03 拦截器

    (1) 创建被访问的资源,后台管理页面manager.jsp,将其放到WEB-INF下,使外部不能直接访问。


    (2) 配置struts.xml,<action name="auth">不配置class类则直接放行,将请求转发到被访问资源/WEB-INF/page/manage.jsp。

<action name="auth">
  <result>/WEB-INF/page/manage.jsp</result>
  <result name="login">/ex03/login.jsp</result>
</action>

    其中,<action>中不配置class,会使用默认的class,在struts-default.xml可以找到:

Struts2笔记 - 03 拦截器

    而默认的class,默认执行execute(),也是返回成功。

    (3) 创建登陆页面login.jsp,接收用户登录信息。

<body>
  <h2>用户登录</h2>
  
  <form action="weblogin.action" method="post">
    用户名: <input type="text" name="username" /><br>
    密码:<input type="password" name="password" />${loginError}<br>
    <input type="submit" value="登录" />  <br>
  </form>
</body>


    (4) 创建LoginAction.java,处理登陆请求校验登录信息,并将有效登陆信息或错误信息放到session中。

import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport implements SessionAware {
  private static final long serialVersionUID = 1L;

  private String username;
  private String password;
  private Map<String, Object> session;

  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String login() {
    if ("admin".equals(username) && "123".equals(password)) {
      session.put("loginInfo", username);
      return SUCCESS;
    } else {
      session.put("loginError", "用户名密码不正确");
      return ERROR;
    }
  }
}

     其中,有效登录信息loginInfo用于步骤5拦截器,校验会话信息错误时,信息用于前台页面展示。


    (5) 创建拦截器AuthInterceptor.java,对访问authAction的请求进行拦截处理,通过ActionContext 获取会话session并校验session的登陆信息。如果不为空,即获取权限放行,否则拦截并转发到登陆界面。

import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthInterceptor extends AbstractInterceptor {

  private static final long serialVersionUID = 1L;

  @Override
  public String intercept(ActionInvocation invocation) throws Exception {

    ActionContext context = ActionContext.getContext();
    Map<String, Object> session = context.getSession();

    if (session.get("loginInfo") != null) {
      String result = invocation.invoke();
      return result;
    } else {
      return "login";
    }
  }
}


    (6) 配置struts.xml,设置拦截器栈。

<struts>
  <package name="default" namespace="/" extends="struts-default">
    <interceptors>
      <interceptor name="mytimer" class="com.ex02_Interceptor.interceptor.TimeInterceptor" />
      <interceptor name="auth" class="com.ex03_auth.AuthInterceptor"></interceptor>
      <!-- 自定义拦截器栈 -->
      <interceptor-stack name="myStack">
        <interceptor-ref name="defaultStack"></interceptor-ref>
        <interceptor-ref name="auth"></interceptor-ref>
      </interceptor-stack>
    </interceptors>

    <!-- 通过此action访问后台页面,需要判断用户是否登录 -->
    <action name="auth">
      <result>/WEB-INF/page/manage.jsp</result>
      <result name="login">/ex03/login.jsp</result>
      <!-- 应用自定义拦截器栈 -->
      <interceptor-ref name="myStack"></interceptor-ref>
    </action>
    <action name="weblogin" class="com.ex03_auth.LoginAction" method="login">
      <result>/WEB-INF/page/manage.jsp</result>
      <result name="error">/ex03/login.jsp</result>
    </action>
  </package>
</struts>


    (7) 测试。


以上是关于Struts2笔记 - 03 拦截器的主要内容,如果未能解决你的问题,请参考以下文章

struts2学习笔记之十三:自定义过滤器

Struts2学习笔记四:Struts2拦截器学习拦截器实现案例Struts2标签学习

Struts2学习笔记

struts2框架学习笔记6:拦截器

笔记:Struts2 拦截器

Struts2-学习笔记系列(14)-拦截器