拦截器的介绍
什么是拦截器?
当一个请求到服务端首先在MVC模式下会执行到Action来响应一个请求,而拦截器是在Action之前被调用的一个对象(Class)。拦截器就是一个类
一、Struts2拦截器原理:
Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的 拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
比如:应用要求用户登陆,且必须为指定用户名才可以查看系统中某个视图资源;否则,系统直接转入登陆页面。对于上面的需求,可以在每个Action的执行实际处理逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用。因为大部分Action里的权限检查代码都大同小异,故将这些权限检查的逻辑放在拦截器中进行将会更加优雅。
PS:
1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.
2. 拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
三大注意事项
1.只能拦截action,不能拦截jsp;
2.拦截器的执行顺序 取决于 引用的顺序;
3.一旦有了自定义拦截器,则 所有系统拦截器都自动失效!
拦截器的生命周期
如何新建一个自定义的拦截器?
定义一个拦截器的初始化模板
package com.ycxy.config;
public class LoginFilter extends MethodFilterInterceptor{
private static final long serialVersionUID = 1L;
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception{
}
}
自定义的拦截器如何配置在struts.xml框架主配置文件里呢?
如登录拦截器的实现类如下(范例)
package com.ycxy.config;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class LoginFilter extends MethodFilterInterceptor{
private static final long serialVersionUID = 1L;
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception{
//获取用户是否登陆
String username = (String)ServletActionContext.getRequest().getSession().getAttribute("CURRENT_LOGIN_USER");
System.out.println("##########interceptor#############");
if(username != null) {
//已登陆放行
return invocation.invoke();
}
//未登陆跳转到登陆页面
return "login";
}
}
拦截器的配置
<!--拦截器的配置 -->
<interceptors>
<!-- 自定义的拦截器配置 -->
<interceptor name="LoginFilter" class="com.ycxy.config.LoginFilter"></interceptor>
<!-- 配置拦截器的堆栈 -->
<intercepotr-stack name="setStack">
<!-- 执行Struts2的默认堆栈 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="loginFilter"></interceptor-ref>
</intercepotr-stack>
</interceptors>
拦截器在Action里使用的话,也需要配置。如下
<interceptor-ref name="LoginFilter"></interceptor-ref> <!-- 执行自定义拦截器 -->
<interceptor-ref name="defaultStack"></interceptor-ref> <!-- 执行struts里默认的拦截器的堆栈 -->
全局返回配置标签的使用
<global-results>
<result name="success">/login.jsp</result>
</global-results>
二、Struts2 拦截器接口实现:
Struts2规定用户自定义拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口。该接口声明了3个方法,其中,init和destroy方法会在程序开始和结束时各执行一遍,不管使用了该拦截器与否,只要在struts.xml中声明了该Struts2拦截器就会被执行。intercept方法就是拦截的主体了,每次拦截器生效时都会执行其中的逻辑。
void init();
void destroy();
String intercept(ActionInvocation invocation) throws Exception;
1:所有拦截器都使用接口Interceptor ,Action去实现这个接口;
Init()方法:在服务器起动的时候加载一次,并且只加载一次;
Destroy()方法:当拦截器销毁时执行的方法;
Interceptor()方法:其中里边有一个参数invocation;
public String intercept(ActionInvocation invocation) throws xception {
System.out.println("interceptor!!");
String result=invocation.invoke();
return result;
}
其中intercept方法是拦截器的核心方法,所有安装的拦截器都会调用之个方法。在Struts2中已经在struts-default.xml中预定义了一些自带的拦截器,如timer、params等。如果在<package>标签中继承struts-default,则当前package就会自动拥有struts-default.xml中的所有配置。
Invocation.invoke()是如果只有一个拦截器执行完这个方法后,会返回给视图,如果有多个拦截器,它顺序的执行完所有的拦截器,才返回给视图,也就是调用后面的action继续执行。
二、Struts2 拦截器详细配置:
默认拦截器是在不设置任何拦截器的时候,给予默认设置的,当只要设置任何一个拦截器就会覆盖掉默认拦截器, 故此,我们需要手动设置
一旦实现了检查拦截器,就可以在所有需要实现权限控制的Action中复用上面的拦截器。
为了使用该拦截器,首先在struts.xml文件中定义拦截器,定义拦截器的配置片段如下:
<!-- 用户拦截器定义在该元素下 -->
<interceptors>
<!-- 定义了一个名为authority的拦截器 -->
<interceptor name="authority" class="lee.AuthorityInterceptor"/>
</interceptors>
定义了该拦截器之后,可以在Action中应用该拦截器,应用该拦截器的配置片段如下:
<!-- 定义一个名为viewBook的Action,其实现类为ActionSupport -->
<action name="viewBook">
<!-- 返回success视图名时,转入/WEB-INF/jsp/viewBook.jsp页面 -->
<result>/WEB-INF/jsp/viewBook.jsp</result>
<!-- 拦截器一般配置在result元素之后! -->
<interceptor-ref name="defaultStack"/>
<!-- 应用自定义拦截器 -->
<interceptor-ref name="authority"/>
</action>
上面名为viewBook的Action,没有指定class属性,默认使用ActionSupport类,配置该Action时,只是指定了一个Result,指定返回success字符串时,系统将转入/WEBINF/jsp/viewBook.jsp页面。但并为未配置login视图对应的JSP页面。
考虑到这个拦截器的重复使用,可能在多个Action都需要跳转到login逻辑试图,故将login Result定义成一个全局Result。下面是配置login Result的配置片段:
<!-- 定义全局Result -->
<global-results>
<!-- 当返回login视图名时,转入/login.jsp页面 -->
<result name="login">/login.jsp</result>
</global-results>
经过上面的配置,如果浏览者在浏览器中直接发送viewBook请求,将会转入如图所示的页面。
这种通过拦截器进行权限控制的方式,显然具有更好的代码复用。
如果为了简化struts.xml文件的配置,避免在每个Action中重复配置该拦截器,可以将该拦截器配置成一个默认拦截器栈(这个默认拦截器栈应该包括default-stack拦截器栈和权限检查拦截器)。
定义自己的默认拦截器栈的配置片段如下:
<interceptors>
<!-- 定义权限检查拦截器 -->
<interceptor name="authority" class="lee.AuthorityInterceptor"/>
<!-- 定义一个包含权限检查的拦截器栈 -->
<interceptor-stack name="mydefault">
<!-- 定义拦截器栈包含default-stack拦截器栈 -->
<interceptor-ref name="default-stack"/>
<!-- 定义拦截器栈包含authority拦截器 -->
<interceptor-ref name=" authority"/>
</interceptor- stack >
</interceptors>
一旦定义了上面的mydefault拦截器栈,这个拦截器栈包含了权限检查拦截器和系统默认的拦截器栈。如果将这个拦截器栈定义成默认拦截器,则可以避免在每个Action需要重复定义权限检查拦截器。
下面是定义默认拦截器的配置片段:
<default-interceptor-ref name="mydefault"/>
一旦在某个包下定义了上面的默认拦截器栈,在该包下的所有Action都会自动增加权限检查功能。对于那些不需要使用权限控制的Action,将它们定义在另外的包中——这个包中依然使用系统原来的默认拦截器栈,将不会有权限控制功能。
PS:拦截器,拦截器栈和默认的拦截器之间的关系
1:拦截器和拦截器栈是一个级别的,也就是说一个拦截器栈中包括许多拦截器, 一个拦截器栈中还可以包括许多拦截器栈,配置如下方式:
<interceptors>
<!-- 先定义拦截器 -->
<interceptor name="myInterceptor" class="com.struts2.interceptor.MyInterceptor">
<!-- 指定系统初始化给拦截器的参数 -->
<param name="hello">张--</param>
</interceptor>
<!-- 加到自己设置的拦截器栈里边去 -->
<interceptor-stack name="myStack">
<interceptor-ref name="myInterceptor">
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
拦截器的使用:
1.先定义;
2.在引用使用;
<interceptor name="myInterceptor" class="com.struts2.interceptor.MyInterceptor">
<interceptor-ref name="myInterceptor">
</interceptor-ref>
2:struts2中有一个系统默认的拦截器栈是 defaultStack,如果你手动引用自己的拦截器,系统默认的拦截器栈将不起作用;这样必需手动引入系统的拦截器栈<interceptor-ref name="defaultStack">
</interceptor-ref>
如果想改变系统默认的拦截器栈,可以这样配置:
<default-interceptor-ref name="myStack">
</default-interceptor-ref>其中myStack是自己定义的拦截器栈名字;
如果拦截器栈中有多个拦截器,在执行action之前的顺序跟配置拦截器的顺序一致,而在action之后执行的顺序是相反的;
PS:最后还附加一点过滤器的东西
过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符
拦截器,是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
拦截器与过滤器的区别 :
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2、拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
6、执行顺序 :过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。
过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。
一个Filter 可负责拦截多个请求或响应:一个请求或响应也可被多个请求拦截。
创建一个Filter 只需两个步骤:
(1)创建Filter 处理类:
(2)在web.xml 文件中配置Filter 。
创建Filter 必须实现javax.servlet.Filter 接口,在该接口中定义了三个方法。
• void init(FilterConfig config): 用于完成Filter 的初始化。
• void destroy(): 用于Filter 销毁前,完成某些资源的回收。
• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 实现过滤功能,该方法就是对每个请求及响应增加的额外处理。
过滤器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驱动。在servlet2.4中,过滤器同样可以用于请求分派器,但须在web.xml中声明,<dispatcher>INCLUDE或FORWARD或REQUEST或ERROR</dispatcher>该元素位于filter-mapping中。
1. 拦截器
要使用拦截器,首先要对它进行配置。拦截器的配置是在 struts.xml 文件中完成的,它通常以 <interceptor> 标签开头,以 </interceptor> 标签结束。定义拦截器的语法格式如下所示:
- <interceptor name="interceptorName" class="interceptorClass">
- <param name="paramName">paramValue</param>
- </interceptor>
上述语法格式中,<interceptor> 元素的 name 属性用于指定拦截器的名称,class 属性用于指定拦截器的实现类。有时,在定义拦截器时需要传入参数,这时需要使用 <param> 标签,其中 name 属性用于指定参数的名称,paramValue 表示参数的值。
2. 拦截器栈
在实际的项目开发中,经常需要在 Action 执行之前执行多个拦截动作,如登录日志记录、权限管理等。
为了方便代码管理和程序的执行,开发者通常会将这些拦截器组成一个拦截器栈,在使用时,可以将栈内的多个拦截器当成一个整体引用。当拦截器栈被附加到一个 Action 上时,在执行 Action 之前必须先执行拦截器栈中的每一个拦截器。
定义拦截器栈使用 <interceptors> 元素和 <interceptor-stack> 子元素,当配置多个拦截器时,需要使用 <interceptor-ref> 元素指定多个拦截器,配置语法如下所示:
- <interceptors>
- <interceptor-stack name="interceptorStackName">
- <interceptor-ref name="interceptorName"/>
- ...
- </interceptor-stack>
- </interceptors>
在上述语法中,interceptorStackName 值表示配置的拦截器栈的名称;interceptorName 值表示拦截器的名称。除此之外,在一个拦截器栈中还可以包含另一个拦截器栈,示例代码如下所示:
- <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>
在上述代码中,定义的拦截器栈的名称是 myStack,在 myStack 栈中,除了引用了两个自定义的拦截器 interceptor1 和 interceptor2 以外,还引用了一个内置拦截器栈 defaultStack,这个拦截器是必须要引入的。
3. 默认拦截器
如果想对一个包下的 Action 使用相同的拦截器,则需要为该包中的每个 Action 都重复指定同一个拦截器,这样写显然过于繁琐。为了解决此问题,Struts2 中支持使用默认拦截器,它可以对其指定的包中的所有 Action 都起到拦截作用。
一旦为某一个包指定了默认拦截器,并且该包中的 Action 未显示指定拦截器,则会使用默认拦截器。反之,若此包中的 Action 显示的指定了某个拦截器,则该默认拦截器将会被屏蔽。此时,如果还想使用默认拦截器,则需要用户手动配置该默认拦截器的引用。
配置默认拦截器需要使用 <default-interceptor-ref> 元素,此元素为 <package> 元素的子元素。其语法格式如下所示:
<default-interceptor-ref name="拦截器(栈)的名称"/>
在上述语法格式中,name 属性的值必须是已经存在的拦截器或拦截器栈的名称。下面用该语法格式配置一个默认拦截器,示例代码如下所示:
- <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>
- <!--配置包下的默认拦截器,既可以是拦截器,也可以是拦截器栈-->
- <default-interceptor-ref name="myStack"/>
- <action name="login" class="com.mengma.action.LoginAction">
- <result name="input">/login.jsp</result>
- </action>
- </package>
在上述代码中,指定了包下面的默认拦截器为一个拦截器栈,该拦截器栈将会作用于包下所有的 Action。
注意:每一个包下只能定义一个默认拦截器,如果需要多个拦截器作为默认拦截器,则可以将这些拦截器定义为一个拦截器栈,再将这个拦截器栈作为默认拦截器即可。