Struts2之拦截器
时间:2017-1-11 11:12
——拦截器(interceptor)
1、拦截器介绍
Struts2拦截器使用的是源自Spring AOP的思想。
AOP的底层实现就是动态代理。
拦截器采用“责任链”模式:
* 在责任链模式里,很多对象都由每一个对象对其下家的引用而连接在一起形成一条链。
* 责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行。
在Struts中可以定义多个拦截器,将多个拦截器按照特定顺序组成拦截栈(顺序调用栈中的每一个拦截器)。
Struts2在struts-default.xml文件中声明了所有的拦截器,而Struts2默认使用的是defaultStack这个拦截器栈,在这个拦截器栈中使用了18个拦截器。简单来说Struts2框架在默认情况下加载了18个拦截器(并没有加载全部拦截器)。
2、使用拦截器可以做什么
可以使用拦截器进行控制Action的访问,例如:权限操作。
3、在Struts2中怎样使用拦截器
1)创建Interceptor
创建一个Java类,实现接口:com.opensymphony.xwork2.interceptor.Interceptor,并重写接口中的三个方法:
* init():该方法将在拦截器被创建后立即被调用,它在拦截器的生命周期内只被调用一次,可以在该方法中对相关资源进行必要的初始化操作。
* intercept():每拦截一个动作请求,该方法都会被调用一次。
> 使用ActionInvocation的实例调用invoke()方法进行放行操作。
> 如果不想放行,可以return一个视图。
* destory():该方法将在拦截器被销毁之前调用,它在拦截器的生命周期内只被调用一次。
2)声明Interceptor
在struts-default.xml文件中声明了拦截器,但是不能在该文件中添加。
需要在struts.xml文件中添加,因为struts.xml继承了struts-default.xml。
在<package>标签下添加:
<package ...>
<interceptors>
<interceptor name="myinterceptor" class="com.wyc.interceptor.MyInterceptor" />
</interceptors>
</package>
3)引用Interceptor
在Action下通过<interceptor-ref>来引用拦截器:
<action name="demo1" class="com.wyc.action.Demo1Action">
<interceptor-ref name="myinterceptor" />
</action>
4)只要显式声明一个拦截器,那么默认的18个拦截器都会失效,可以再手动引入:
在<action>标签下添加:
<action ...>
<interceptor-ref name="defaultStakc" />
</action>
因为struts.xml中的package是struts-default.xml的子类,所以可以直接使用父类内容。
谁先引入谁先调用。
5)将拦截器写在<action>下,如果拦截器个数过多,会导致阅读不方便,可以引入一个拦截器栈:
* 在struts.xml下的<package>下的<interceptors>中定义一个拦截器栈:
<package ...>
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="my" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
</package>
* 在<action>中引入拦截器栈:
<action>
<interceptor-ref name="myStack" />
</action>
相当于将拦截器打包然后统一传递给<action>。
4、Interceptor接口
* Struts2会依次调用程序员为某个Action注册的每一个拦截器的intercept()方法(通过递归实现)。
* 每次调用intercept()方法时,Struts2会传递一个ActionInvocation接口的实例(在invoke()方法中通过该实例递归调用invoke()方法)。
* ActionInvocation:代表一个给定动作的执行状态,拦截器可以从该类的对象中获得与该动作相关联的Action对象和Result对象,在完成拦截器自己的任务之后,拦截器将调用ActionInvocation对象的invoke()方法前进到Action处理流程的下一个环节。
* 还可以调用ActionInvocation对象的addPreResultListener()方法给ActionInvocation对象注册一个或多个PreResultListener监听器,该监听器对象可以在动作执行完毕后,开始执行动作结果之前做些事情。
* AbstractInterceptor类实现了Interceptor接口,并为int()、destory()方法提供了一个空白的实现。
5、Struts2自带拦截器介绍
3、分析拦截器原理
源代码执行流程
1)Struts2框架开始执行的位置是在xml文件中配置的过滤器,所以在StrutsPrepareAndExecuteFilter中查找:
在doFilter()方法中有一行代码:ExecuteOperations execute.executeAction(request, response, mapping),执行Action操作。
2)在executeAction()方法执行过程中会访问Dispatcher类中的serviceAction()方法,在这个方法中会创建一个动态代理对象:
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
proxy就是Action的代理对象。
3)然后查看ActionInvocation的代理方法
发现ActionInvocation是一个接口,可以查看其子类:DefaultActionInvocation。
在动态代理中,InvocationHandler负责调用方法,而ActionInvocation就相当于继承了InvocationHandler。
在DefaultActionInvocation中有这么一段代码:
// 得到拦截器对象然后调用拦截方法,进行拦截操作,并传入自己的对象。
其中interceptors是一个Iterator<Interceptor Mapping>迭代器,其中保存着所有的拦截器。
通过源代码分析,发现DefaultActionInvocation中是通过递归完成所有拦截器的调用操作。
当程序开始执行时,会将所有的拦截器存入一个List,然后在DefaultActionInvocation的invoke()方法中进行迭代,如果集合不为空,则会取出拦截器对象然后调用拦截方法,所以会调用自定义拦截器对象的方法,然后传入当前类的对象。在执行完拦截操作后,会继续由“传入的当前类对象”调用invoke()方法,直至集合为空,这是递归操作。
4、案例——权限控制
通过login.jsp访问LoginAction,登录成功后将用户存储到session中,然后跳转到book.jsp
在book.jsp中提供CRUD链接,每一个链接都可以访问BookAction中的对应方法。
要求:对于BookAction中的add,update,delete方法必须要求用户登录后才能访问,search无要求。
问题:怎样解决只控制Action中某些方法的拦截操作?
1)创建类,不实现Interceptor接口,而是继承其下的一个子类:MethodFIlterInteceptor
不需要重写intercept()方法,而是重写doIntercept()方法,因为子类已经重写了intercept()方法,并在该方法中调用了doIntercept()方法。
2)在struts.xml文件中声明:
<interceptors>
<interceptor name="" class="">
<!-- 可以在这里为MethodFilterInterceptor类的参数进行赋值 -->
<param name="includeMethods">add, update, delete</param> // 表示对这些方法进行拦截
<param name="excludeMethods">search</param> // 表示不对该方法进行拦截操作
</interceptor>
</interceptors>
3)MethodFilterInterceptor部分源码
public abstract class MethodFilterInterceptor extends AbstractInterceptor {
4)实现:
JSP页面:
* login.jsp
提供登录表单
* book.jsp
提供CRUD四个超链接,分别访问BookAction的CRUD方法。
* error.jsp
显示错误信息
* success.jsp
显示成功信息
Action:
* LoginAction
校验用户名密码,登录成功后将用户保存到session中,然后跳转到book.jsp
* public class LoginAction extends ActionSupport implements ModelDriven {
* BookAction
声明CRUD方法,设置返回视图。
Interceptor:
* BookInterceptor
继承MethodFilterInterceptor类,重写doIntercept()方法。
* public class BookInterceptor extends MethodFilterInterceptor {
Domain:
* User
提供用户名、密码。
struts.xml
* 配置自定义拦截器,分别设置CRUD需要拦截的方法。
——总结
1、拦截器介绍
拦截器使用了AOP的思想
采用了责任链模式
使用的是defaultStack拦截器栈
2、拦截器的使用
如何定义拦截器
如何注册拦截器
如何指定需要拦截的方法
3、拦截器的原理
分析源代码的执行流程
4、Interceptor与Filter的区别