拦截器(interceptor)用于AOP编程中某个字段或者方法访问前以及被访问后加入某些操作,拦截是AOP的一种实现策略。拦截器的使用还有一个重要的概念,就是拦截器链(InterceptorChain),意味着被使用的拦截器将会按照一定的顺序形成一条链,在访问被拦截的方法或者字段时就会被顺序调用。
拦截器的实现原理
拦截器大多是通过代理方式实现,Struts2的实现方式相对较为简单。当请求Struts2的StrutsPrepareAndExcuteFilter,也就是我们的前端控制器,前端控制器会访问配置文件,根据配置实例化所需的拦截器,并将拦截器对象放入一个列表,最后依次调用拦截器。Struts2的拦截器是可插拔的。
自定义拦截器的实现
1.实现com.opensymphony.xwork2.interceptor.Interceptor
接口,该接口有三个方法:
+ init()
:初始化方法,在拦截器被创建之时被调用,在Interceptor的生命周期中只会被调用一次,该方法内可以进行相关资源的初始化;
+ intercept(ActionInvocation invocation)
:拦截器的核心方法,它返回一个字符串作为逻辑视图,系统根据其返回的字符串跳转到相应的视图资源。每拦截一次动作请求,该方法就被执行一次。该方法的ActionInvocation参数包含了被拦截的action的引用,通过该参数的invoke方法,将控制权交给下一个拦截器或者Action的excute方法。
+ destroy()
:销毁方法,用来释放该拦截器相关的资源。在Interceptor的生命周期内同样只被执行一次。
它的生命周期与项目是一致的。(Servlet是在第一次访问时创建,随着项目的关闭而销毁。)
2. 继承AbstractInterceptor
类,该类为抽象类,查看其源码,该类实现了Interceptor接口。这样就不需要实现不必要的初始化和销毁方法。
3. 继承MethodFilterInterceptor
类,该类能够指定拦截器拦截的方法,以及不需被拦截的方法。
Struts2拦截器链的形成
拦截器的核心拦截方法的传入参数是ActionIvocation,它是一个抽象接口,在DefaultActionInvocation实现的invoke方法中,会判断其
//interceptors是一个拦截器列表
//判断该列表是否调用结束
if (this.interceptors.hasNext()) {
InterceptorMapping interceptor = (InterceptorMapping)this.interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
//没有执行结束,则继续调用下一个拦截器
try {
this.resultCode = interceptor.getInterceptor().intercept(this);
} finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
//执行结束,反射方式执行action
//resultCode是Action的执行结果
this.resultCode = this.invokeActionOnly();
}
而在所有的预置的Interceptor内的intercept方法内,都有如下源码:
public String intercept(ActionInvocation invocation) throws Exception {
...
...
...
return invocation.invoke();
}
这使得拦截器与invocation之间相互调用形成了类似于循环的作用,最终能够确保将List中的Interceptor执行完毕。
因此,将拦截方法内容置于invocation.invoke()前,则该自定义拦截器为前处理拦截器,置于之后则该拦截器为后处理拦截器。
拦截器的最终返回结果是视图逻辑名(也就是各种action的执行result的name属性),如果自定义拦截器中不使用invacation.invoke()获得放行的结果,直接返回字符串(这通常意味着请求并没有通过拦截器的验证逻辑),那么Struts2会根据对应的视图逻辑名返回视图资源。
就关于视图逻辑名,其实在SpringMVC中,我们在Controller类中,返回的字符串 ,通常也是视图逻辑名。