Struts2拦截器介绍
Interceptor拦截器类似于过滤器,是可以在action执行前后执行的代码。是我们做web开发时经常用的技术。比如:权限控制、日志等。我们也可以将多个Interceptor连在一起组成Interceptor栈。
拦截器的工作原理如上图,每一个Action请求都包装在一系列的拦截器的内部。拦截器可以在Action执行直线做相似的操作也可以在Action执行直后做回收操作。
每一个Action既可以将操作转交给下面的拦截器,Action也可以直接退出操作返回客户既定的画面。
Struts2拦截器,每个拦截器类只有一个对象实例,即采用单例模式,所以引用这个拦截器的Action都共享这一拦截器类的实例,因此,在拦截器中如果使用类变量,要注意同步问题。
实现原理 : Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对应拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
最基本的bean的注入就是通过默认的拦截器实现的,一般在struts2.xml的配置中,package内直接或间接继承了struts-default.xml,这样struts2默认的拦截器就会作用。
拦截器和过滤器的区别
-
拦截器和过滤器的概念非常类似。
-
过滤器隶属于web容器,可以过滤一切请求(包括action、servlet、jsp、html等等)。
-
而拦截器隶属于struts2框架,只能拦截action(无法拦截对jsp的请求)。
-
过滤器内部采用函数回调来实现。拦截器采用动态代理来实现。
拦截器在struts2中的应用
对于Struts2框架而言,正是大量的内置拦截器完成了大部分操作。比如:
– params拦截器将http请求中参数解析出来赋值给Action中对应的属性。
– Servlet-config拦截器负责把请求中HttpServletRequest实例和HttpServletResponse实例传递给Action
– …
struts-default.xml中有一个默认的引用,在默认情况下(也就是<action>
中未引用拦截器时)会自动引用一些拦截器。
附:Struts2(XWork)提供的拦截器的功能说明
自定义拦截器
在开始着手创建自定义拦截器前,切记以下原则:
拦截器必须是无状态的,不要使用在API提供的ActionInvocation之外的任何东西。要求拦截器是无状态的原因是Struts 2不能保证为每一个请求或者action创建一个实例,所以如果拦截器带有状态,会引发并发问题。
如何自定义拦截器?
- 实现接口Interceptor或者继承类AbstractInterceptor ,并重写public String intercept(ActionInvocation invocation) 。
- 在sturts.xml中使用
<interceptor>
元素来定义拦截器 。 - 在配置action时使用
<interceptor-ref>
元素来使用拦截器。
注意:如果为Action指定了一个拦截器,则系统默认的拦截器栈将会失去作用。为了继续使用默认拦截器,所以配置文件中需要手动引入默认拦截器(见示例)。
另外,可以将多个拦截器合并在一起作为一个堆栈调用,当一个拦截器堆栈被附加到一个Action的时候,要想Action执行,必须执行拦截器堆栈中的每一个拦截器(见示例)。
Interceptor接口声明了三个方法:
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
Init方法在拦截器类被创建之后,在对Action镜像拦截之前调用,相当于一个post-constructor方法,使用这个方法可以给拦截器类做必要的初始话操作。
Destroy方法在拦截器被垃圾回收之前调用,用来回收init方法初始化的资源。
Intercept是拦截器的主要拦截方法,如果需要调用后续的Action或者拦截器,只需要在该方法中调用invocation.invoke()方法即可,在该方法调用的前后可以插入Action调用前后拦截器需要做的方法。如果不需要调用后续的方法,则返回一个String类型的对象即可,例如Action.SUCCESS,在struts.xml中配置相应的result标签,控制返回视图。
另外AbstractInterceptor提供了一个简单的Interceptor的实现,这个实现为:
public abstract class AbstractInterceptor implements Interceptor {
public void init() {
}
public void destroy() {
}
public abstract String intercept(ActionInvocation invocation) throws Exception;
}
在不需要编写init和destroy方法的时候,只需要从AbstractInterceptor继承而来,实现intercept方法即可。
示例
考虑如下需求:在实现商城购物时,我们希望用户在登陆之后才能够进行商品的浏览、添加购物车等操作,在未登录状态下点击购物车等页面会跳转到登录界面让用户登录。
使用拦截器的解决思路:在用户登录之后,User对象存放在Session当中,未登录时Session中不存在User对象。故在拦截器中可判断Session中是否存在User对象,不存在则返回登录视图,存在则执行invoke方法,即请求会被action正常响应,并返回相应的结果。
1.编写Interceptor类
public class LoginInterceptor extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Map<String, Object> session = invocation.getInvocationContext().getSession();
User user=(User)session.get("user");
if(user==null) {
System.out.println("请先登录!");
return "login";
}
return invocation.invoke();
}
}
2.在struts.xml中注册拦截器
<package name="interceptor" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="doLoginInterceptor" class="com.xintao.learnContext.interceptor.LoginInterceptor"/>
<interceptor-stack name="loginInterceptor"> <!--拦截器栈-->
<interceptor-ref name="doLoginInterceptor"/>
<interceptor-ref name="defaultStack"/> <!--引入默认拦截器-->
</interceptor-stack>
</interceptors>
</package>
3.在对应的action中使用<interceptor-ref>
标签使用拦截器
<package name="learnContext" extends="interceptor" namespace="/">
<action name="welcome" class="com.xintao.learnContext.controller.LoginAction" method="welcome">
<interceptor-ref name="loginInterceptor"/>
<result name="login">/login.jsp</result>
<result name="welcome">/welcome.jsp</result>
</action>
</package>
上述步骤是将拦截器及拦截器栈声明为了全局变量,注意在第3步中,action的package中的extends元素不再是“struts-default”,变为其引用的拦截器,该拦截器所在package名称。
也可在action中声明拦截器,第一步与上述方法相同,第2、3步可合在一个package中书写。
另一种struts.xml配置方法(较常用):
<package name="learnContext" extends="struts-default" namespace="/">
<interceptors>
<interceptor name="doLoginInterceptor" class="com.xintao.learnContext.interceptor.LoginInterceptor"/>
<interceptor-stack name="loginInterceptor">
<interceptor-ref name="doLoginInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="welcome" class="com.xintao.learnContext.controller.LoginAction" method="welcome">
<interceptor-ref name="loginInterceptor"/>
<result name="welcome">/welcome.jsp</result>
<result name="login">/login.jsp</result>
</action>
</package>
注意事项
1.interceptor-ref内可以定义param元素,即实现对特定方法的拦截;但是注意,使用方法拦截器必须直接或间接实现AbstractInerceptor接口或者继承MethodFilterInterceptor类,并重写doIntercept方法.param标签内有两个属性:
excludeMethods:排除的方法
includeMethods:包含的方法
2.每一个拦截器都可以配置参数,有两种方式配置参数,一是针对每一个拦截器定义参数,二是针对一个拦截器堆栈统一定义所有的参数,例如:
<interceptor-ref name="validation">
<param name="excludeMethods">myValidationExcudeMethod</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
或者
<interceptor-ref name="defaultStack">
<param name="validation.excludeMethods">myValidationExcludeMethod</param>
<param name="workflow.excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
参考:
https://www.cnblogs.com/yw-ah/p/5761235.html
https://www.cnblogs.com/withyou/p/3170440.html