Struts2拦截器Interceptor的原理与配置实例详解
一、struts2拦截器原理:
struts2拦截器的实现原理相对简单,当请求struts2的action时,struts 2会查找配置文件,并根据其配置实例化相对的 拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
比如:应用要求用户登陆,且必须为指定用户名才可以查看系统中某个视图资源;否则,系统直接转入登陆页面。对于上面的需求,可以在每个action的执行实际处理逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用。因为大部分action里的权限检查代码都大同小异,故将这些权限检查的逻辑放在拦截器中进行将会更加优雅。
ps:
1. struts2拦截器是在访问某个action或action的某个方法,字段之前或之后实施拦截,并且struts2拦截器是可插拔的,拦截器是aop的一种实现.
2. 拦截器栈(interceptor stack)。struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
二、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中。
实例教程
下面写一个自定义拦截器的例子,判断用户是否登录,就是检查当前用户的session中的user属性是否为空,如果为空,就跳到登录页面,否则,继续执行.
1.编写拦截器,在interceptor包下常见一个java类,名为logininterceptor,继承abstractinterceptor:
public class logininterceptor extends abstractinterceptor{ @override public string intercept(actioninvocation invocation) throws exception { //得到拦截到的action的名称,看是否是login,当是login的时候,不用进行下面的检测了,直接执行下一个拦截器 string actionname=invocation.getproxy().getactionname(); if("login".equals(actionname)){ return invocation.invoke(); } //如果不是login.则判断是否已登录,及检测session中key为user的值是否存在,如果不存在,跳回到登录页面 string user=(string)invocation.getinvocationcontext().getsession().get("user"); if(user==null){ system.out.println("未登录"); return "login"; } //进行到这里.说明用户已登录,则跳转到下一个拦截器 return invocation.invoke(); } }
2,在struts.xml中配置interceptor,主要特别注意的是,当使用了自定义的拦截器后,默认拦截器将不起作用,默认拦截器实在struts-default.xml中配置的,当引用了自定义拦截器,又想使用struts2提供的默认拦截器功能,需要手动配置:这里我将默认拦截器和我写的进行登录权限验证的拦截器,写到一个拦截器栈里,然后调用这个默认拦截器栈:
<package name="default" namespace="/" extends="struts-default"> <interceptors> <!-- 配置自定义的拦截器--> <interceptor name="checklogin" class="com.wang.interceptor.logininterceptor"/> <!--配置一个拦截器栈,里面包含自己定义的拦截器和defaultstack默认拦截器--> <interceptor-stack name="mystack"> <interceptor-ref name="defaultstack"></interceptor-ref> <interceptor-ref name="checklogin"></interceptor-ref> </interceptor-stack> </interceptors> <!--引用默认的拦截器(栈)--> <default-interceptor-ref name="mystack"></default-interceptor-ref> <!--配置一个全局结果集--> <global-results> <result name="login">/login.jsp</result> </global-results> <action name="login" class="com.wang.action.loginaction" > <result>/succ.jsp</result> <result name="error">/login.jsp</result> </action> </package>
这里我使用了默认拦截器标签,即相当于在每个action标签下,使用了 <interceptor-ref name="mystack"></interceptor-ref>.jsp页面和loginaction类这里就省略了.
再来介绍一下方法拦截器,方法拦截器比action拦截器控制的更加精细,大体实现方式和action拦截器相同,不同的是它继承的是methodfilterinterceptor类,重写的是dointerceptor()
方法,在struts.xml的配置上也有些不同,大体是这样:
<interceptor-ref name="methodinterceptor"> <!--配置被拦截的方法--> <param name="includemethods">methoda,methodsb</param> <!--配置不被拦截的方法--> <param name="excludemethods">methodsc,methodsd</param> </interceptor-ref>
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。