Struts2里的拦截器

ε祈祈猫儿з 提交于 2019-12-24 16:47:13

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

拦截器(Interceptor)

一、拦截器概述

  1. 拦截器是Struts2最强大的特征之一,它是一种让用户在Action执行之前和Result执行之后进行 一些处理的机制。
  2. 拦截器在概念上与servlet过滤器或JDK代理类相同。拦截器允许横切功能,把action以及框架分开实现。你可以使用拦截器实现以下操作:
    • 在调用action之前提供预处理逻辑。
    • 在调用action后提供后处理逻辑。
    • 捕获异常,以便可以执行备用处理。
  3. Struts2框架中提供的许多功能都是使用拦截器实现的,包括异常处理,文件上传,生命周期回调和验证等。事实上,由于Struts2将其大部分功能基于拦截器,因此不太可能为每个action分配7个或8个拦截器。

二、拦截器的优点

  1. 简化Action的实现。拦截器能把很多功能从Action中分离出来,大量减少了Action的代码。
  2. 功能更单一,每个拦截器都有自己独特的功能。
  3. 通用代码模块化,将Action中通用的代码抽离出来封装到拦截器里。
  4. 提高重用性,当通用的代码被封装在拦截器中,实现了代码模块化后,就可以对不同的Action,根据所需功能的不同,来配置相符的拦截器。
  5. 实现了AOP。Struts2通过拦截器实现了AOP(面向切面编程),AOP是一种编程范式,他是一种分散实现关注功能的编程方法。

三、预定义的拦截器

  1. 在之前的例子中我们可以看见,在运行Action的execute方法之前,他们的属性已经有值了,而且这些值和用户请求中的参数是一样的,说明有操作在execute执行前就将这些值放入到Action的属性中了,这个功能就是有拦截器来操作的。
  2. 在Struts2中定义了很多拦截器,这些拦截器都在struts-default.xml文件中的struts-default包中
<interceptors>
 <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
 <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
 <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
 <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
 <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
 <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/>
 <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
 <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
 <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
 <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
 <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
 <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
 <interceptor name="i18n" class="org.apache.struts2.interceptor.I18nInterceptor"/>
 <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
 <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
 <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
 <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
 <interceptor name="paramRemover" class="com.opensymphony.xwork2.interceptor.ParameterRemoverInterceptor"/>
 <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInterceptor"/>
 <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
 <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
 <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
 <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
 <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
 <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
 <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
 <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
 <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
 <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
 <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
 <interceptor name="datetime" class="org.apache.struts2.interceptor.DateTextFieldInterceptor" />
 <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
 <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
 <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
 <interceptor name="annotationParameterFilter" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationParameterFilterInterceptor" />
 <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" />
 <interceptor name="noop" class="org.apache.struts2.interceptor.NoOpInterceptor" />
</interceptors>
  1. 下来我们就介绍几个常用的拦截器
序号 拦截器 说明
1 alias 允许参数在请求之间使用不同的别名。
2 checkbox 通过为未检查的复选框添加参数值false,以辅助管理复选框。
3 conversionError 将字符串转换为参数类型的错误信息放置到action的错误字段中。
4 createSession 自动创建HTTP会话(如果尚不存在)。
5 debugging 为开发人员提供一些不同的调试屏幕。
6 execAndWait 当action在后台执行时,将用户发送到中间的等待页面。
7 exception 映射从action到结果抛出的异常,允许通过重定向自动处理异常。
8 fileUpload 便于文件上传。
9 i18n 在用户会话期间跟踪选定的区域。
10 logger 通过输出正在执行的action的名称提供简单的日志记录。
11 params 设置action上的请求参数。
12 prepare 这通常用于执行预处理工作,例如设置数据库连接。
13 profile 允许记录action的简单分析信息。
14 scope 在会话或应用程序范围内存储和检索action的状态。
15 ServletConfig 提供可访问各种基于servlet信息的action。
16 timer 以action执行时间的形式提供简单的分析信息。
17 token 检查action的有效性,以防止重复提交表单。
18 validation 提供action的验证支持。
19 staticParams 将配置文件里定义的Action参数,设置到对应的Action实例中,Action参数使用<param>标签,是<action>的子元素。如果在配置文件里设置的值和请求的值的name是同一个那么以请求的为准。在初始化Action的时候,就会将struts.xml中的参数对应到Action的属性中,然后把用户请求的数据设置到Action实例中相应的属性上。
20 modelDriven r如果Action实现ModelDriven接口,他将getModel()方法取得的模型对象存入到OnglValueStack中
21 chain 将之前一个执行结束的Action属性设置到当前的Action中。他被用在ResultType为chain所指定的结果的Action中,该结果Action对象会从值栈中获取前一个Action对应的属性,他实现了Action之间的数据传送。
预定义的拦截器栈
  1. 我们在使用struts.xml中,是否看见了任何关于拦截器的代码呢?
  2. 虽然用户没有主动的去配置拦截器但是Struts2会使用默认的拦截器
  3. 这些拦截器的声明和引用都在struts-default.xml文件中。前面已经写出了拦截器的定义,这里就只列出他们的拦截器栈的defaultStack拦截栈,他们都是放在同一个包下的struts-default包中。拦截器栈的定义是<interceptors>的直接子元素。
<interceptors>
 <interceptor-stack name="defaultStack">
      <interceptor-ref name="exception"/>
      <interceptor-ref name="alias"/>
      <interceptor-ref name="servletConfig"/>
      <interceptor-ref name="i18n"/>
      <interceptor-ref name="prepare"/>
      <interceptor-ref name="chain"/>
      <interceptor-ref name="scopedModelDriven"/>
      <interceptor-ref name="modelDriven"/>
      <interceptor-ref name="fileUpload"/>
      <interceptor-ref name="checkbox"/>
      <interceptor-ref name="datetime"/>
      <interceptor-ref name="multiselect"/>
      <interceptor-ref name="staticParams"/>
      <interceptor-ref name="actionMappingParams"/>
      <interceptor-ref name="params"/>
      <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-ref name="debugging"/>
 </interceptor-stack>
 </interceptors>
  1. 引用拦截器栈
<package name="struts-default" abstract="true" strict-method-invocation="true">
  <default-interceptor-ref name="defaultStack"/>
</package>
上面的配置可分为四部分
  • <interceptor>

  • <interceptor-stack>

  • <interceptor-ref>

  • <default-interceptor-ref>

预定义拦截器的配置使用
在struts.xml的Action配置,引用需要使用的拦截器
  1. 在action元素中,如何使用<interception-ref>子元素呢?其实很简单,只需要在<action>元素中,配置需要的<interceptor-ref>子元素就可以了,<interceptor-ref>子元素里面配置需要使用的拦截器的名称,比如:
 <action name="interceptor1" class="">
            <param name="name">taeyeon</param>
            <result></result>
            <interceptor-ref name="chainStack"></interceptor-ref>
            <interceptor-ref name="chain"></interceptor-ref>
</action>

  1. <interceptor-ref>子元素的name,不仅仅可以是一个已经定义好的拦截器的名称,还可以是一个已经定义好的拦截器栈的名称,如上的name="chainStack"就是一个拦截器栈。
  2. 其实在配置自己的package的时候所扩展的struts-default包中,就已经定义了一个 <default-interceptor-ref name="defaultStack"/>,正是这个定义,前面的示例中,用户都没有主动去配置拦截器,但是我们还是使用到了拦截器里的功能,只不过用户不知道而已。
拦截器的调用顺序
  1. 首先,要找到他自己声明的拦截器的引用,即<action>元素有没有<interceptor-ref>子元素。
  2. 其次,站到这个<action>所在包有没有声明默认拦截器的引用。即<package>元素的<default-interceptor-ref>子元素。
  3. 最后,递归地寻找这个包的父包,看看有没有声明默认的拦截器引用,直到找到有拦截器引用为止。

这三个关系其实是覆盖关系,同时拥有时,最底层的<action>包里的拦截器引用会覆盖其他的。

自定义的拦截器

什么是自定义的拦截器
  1. 所谓自定义的拦截器,就是由用户自己定义并实现的拦截器,而不是由Struts2定义好的拦截器。
  2. 有时在实际的开发中我们需要使用到自定义的拦截器来为我们实现特定的功能。
开发自定义拦截器
  1. 要实现自定义的拦截器是非常简单的,只要写一个实现Interceptor接口的类就可以了,该接口定义为:
public interface Interceptor extends Serializable {
  void destroy();
  void init();
  String intercept(ActionInvocation invocation) throws Exception;
  • intercept()方法就是拦截器的处理方法,用户要实现的功能主要写在这个方法中。
  1. 在intercept()方法之后书写invocation.invoke();,这句话的意思是继续运行拦截器后续的处理,如果这个拦截器,那么会继续运行,一直运行到Action,然后执行Result。
  2. 如果没有书写invocation.invoke();,那么意思就是执行到当前就结束了,不再继续向后执行,换句话说就是后面的拦截器、Action就不在执行。而是在这里返回Result字符串,直接去运行Result了。
  3. invocation.invoke();,这句话之前写的功能,会在Action运行之前执行。
  4. invocation.invoke();,这句话之后写的功能,会在Result运行之后执行。
  5. intercept方法的返回值就是最终要返回的Result字符集,这只是在前面没有执行Result的时候才有效,也就是前面没有invocation.invoke();,这句话的时候,这个返回值就相当于最终要返回的Result字符串,然后才执行相应的Result处理。
示例
  1. 书写Action处理类,这里我们只是输出语句,在实际开发中要书写实际的功能代码
package struts2.com.interceptor;

import com.opensymphony.xwork2.ActionSupport;

public class InterceptorAction extends ActionSupport {
		private String name;

		public String getName() {
				return name;
		}

		public void setName(String name) {
				this.name = name;
		}

		@Override
		public String execute() throws Exception {
				System.out.println(name);
				System.out.println("我是Action");
				return SUCCESS;
		}
}

  1. 书写Interceptor
package struts2.com.interceptor;

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

public class Interceptor_1 implements Interceptor {
		@Override
		public void destroy() {
				System.out.println("我被销毁了");
		}

		@Override
		public void init() {
				System.out.println("我被初始化了");
		}

		@Override
		public String intercept(ActionInvocation invocation) throws Exception {
				System.out.println("我在Action之前执行");
				invocation.invoke();
				System.out.println("我在Result之后执行");
				return null;
		}
}
  1. 配置xml文件
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">

<struts>
    <package name="interceptor" extends="struts-default">
        <interceptors>
            <interceptor name="myinterceptor" class="struts2.com.interceptor.Interceptor_1"/>
            <interceptor-stack name="mystack">
                <interceptor-ref name="myinterceptor"/>
            </interceptor-stack>
        </interceptors>

        <action name="interceptor1" class="struts2.com.interceptor.InterceptorAction">
            <result>lastinterpetor.jsp</result>
            <interceptor-ref name="mystack"/>
            <interceptor-ref name="defaultStack"/>
        </action>
    </package>
</struts>

我们既然实现了Interceptor接口,那么我们就相当于自定义了一个拦截器,那么我们要在引用的包里声明定义该拦截器,如上面的<interceptors>元素的子元素就是声明拦截器和定义拦截器栈;然后再<action>中使用 <interceptor-ref name="mystack"/>来引用拦截器;这里要是引用了其他的拦截器,那么之前说的继承的包里的默认拦截器栈就不会生效了,因为是默认的,所以当有值的时候就不会再用默认拦截器栈,那么我们还想使用里面的功能,就只能再次引用他了,通过<interceptor-ref name="defaultStack"/> 4. 书写提交、响应页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>interceptor提交</title>
</head>
<body>
<form action="interceptor1.action">
    姓名: <input type="text" name="name">
    <input type="submit" value="提交">
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>interpetor提交之后</title>
</head>
<body>
helloword!
<s:property value="name"/>
<%
    System.out.println("我是Result");
%>
</body>
</html>

如果在xml配置中没用引用默认拦截器,那么这里的name属性的值就传递不到值栈里去了。获取页面也就获取不到该值了 5. 控制台输出

.......
我被初始化了
12月 24, 2019 2:48:59 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-bio-80"]
我在Action之前执行
周杰伦
我是Action
我是Result
我在Result之后执行

通过控制台的输出我们可以看出,在拦截器的init方法中写的输出语句会在服务器启动的时候就被初始化了,而且这个初始化的输出次数是根据xml中对该拦截器的引用次数来定的,比如在xml文件中引用了这个拦截器2此,那么这个init方法就会在启动服务器的时候被调用两次;书写在 invocation.invoke();方法之前的会在Action运行之前执行,然后运行到 invocation.invoke();,会执行Action或者其他拦截器;在 invocation.invoke();之后的代码会在Result执行之后运行。 6. 一定要注意,如果拦截器没有书写 invocation.invoke();方法,那么就相当于该方法后面的代码都不会被执行,而且连Action和其他拦截器都不会执行,将直接返回return的字符串给Result,来匹配Result和运行Result。也要记住拦截器的执行位置是在Action开始之前,Result执行之后,才会执行拦截器的。

拦截器中的参数
  1. 有时间我们也是需要将一些参数传递到拦截器中的,比如
    • 在xml的action元素中配置的拦截器参数
     <action name="paraminterceptor" class="struts2.com.interceptor.InterceptorAction">
             <result>lastinterpetor.jsp</result>
             <interceptor-ref name="defaultStack"/>
             <interceptor-ref name="myinterceptor" >
                 <param name="age">123</param>
     </interceptor-ref>
    
    输出
     我的age:123
    
    param元素的name属性值要和拦截器中的属性值要对应上
    • 在拦截器声明的时候设置参数
     <interceptors>
               <interceptor name="myinterceptor" class="struts2.com.interceptor.Interceptor_1">
                 <param name="213"></param>
             </interceptor>
             <interceptor-stack name="mystack">
                 <interceptor-ref name="myinterceptor"/>
             </interceptor-stack>
         </interceptors>
    
    输出
     我的age:321
    
    拦截器的参数是属于<interceptor-ref>的子元素。
    • 当两种情况下都配置了,那么我们看看是使用那么参数。
    我的age:123
    
    我们可以看出这里面的两个地方都配置的话,其实是按照action元素里面的拦截器配置的参数为主的。我们也可以在拦截器栈里面引用的时候添加参数。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!