Struts 2(六):拦截器
第一节 拦截器介绍以及实现原理
1.1 拦截器简介
拦截器是Struts 2的核心组成部分,拦截器的功能是在执行某一个操作时(或调用某个方法时),它会在执行操作前以及执行操作后进行一系列操作。Struts 2的大部分功能都是通过拦截器来完成的,在拦截器中应用了软件开发中的一个重要思想,那就是面向切面编程,也就是AOP。
Struts 2的拦截器与过滤器Filter非常类似,但是两者拦截的对象不同,拦截器用来拦截业务控制器,也就是说拦截器只能用来拦截使用了该拦截器的Action,而过滤器用来过滤用户请求,对于所有的请求都会进行过滤,如JSP页面、图片文件、CSS文件等
Struts 2通过一种可插拔式的设计来添加或删除拦截器,从而提供了非常好的可扩展性。通过Struts 2提供的内建拦截器可以完成许多的功能,同时开发人员还可以自定义拦截器从而扩展Struts 2框架。
1.2 拦截器实现原理
其实所谓的拦截其实就是动态的生成一个代理对象,在这个代理对象中包含了拦截器方法的调用,当调用该代理对象的方法时,同时会调用拦截器中的方法,通过这样的方法就实现了动态调用拦截器方法的目的。
下面这段代码主要演示如何通过动态代理来实现拦截器,从而了解拦截器的实现原理:
package com.sanqing.interceptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface User { public void up(); //声明搭电梯上楼方法 } class UserImpl implements User{ public void up() { System.out.println("搭电梯上楼"); //实现搭电梯上楼方法 } } class Elevator { public void openDoor() { System.out.println("开门");//开门方法 } public void closeDoor() { System.out.println("关门");//关门方法 } } class MyHandler implements InvocationHandler { private Object user;//被代理目标对象 Elevator ele = new Elevator();//电梯类实例 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result; //结果对象 ele.openDoor(); //调用电梯类的开门方法 result = method.invoke(user, args);//执行用户方法 ele.closeDoor(); //调用电梯类的关门方法 return result; //返回结果对象 } public void setUser(Object user){ //设置目标对象 this.user = user; } } class ProxyFac { public static Object getProxy(Object user) { MyHandler mh = new MyHandler();//创建处理类实例 mh.setUser(user);//传入user实例 //返回代理类实例 return Proxy.newProxyInstance(UserImpl.class.getClassLoader(), UserImpl.class.getInterfaces(), mh); } } public class MyInterceptor { public static void main(String[] args) { User user = new UserImpl();//创建目标对象,用来代理 User proxyUser = (User) ProxyFac.getProxy(user);//通过代理对象获得代理实例 proxyUser.up();//调用代理实例的上楼方法 } }
第二节 自定义拦截器
2.1 通过Interceptor接口实现拦截器
2.2 通过AbstractInterceptor类实现拦截器
2.3 配置拦截器
在配置文件struts.xml的package节点下添加一个interceptors节点,该节点下可以包含多个interceptor子节点,每个interceptor子节点用来配置一个拦截器,通过interceptor节点指定该拦截器的名称和实现类来完成拦截器配置,代码格式如下:
<interceptors> <interceptor name="拦截器名称" class="拦截器实现类"></interceptor> </interceptors>
2.4 使用拦截器
上一小节介绍了如何配置拦截器,但是仅经过上述步骤拦截器还不能起作用,因为业务控制器Action还不知道要使用哪个拦截器,需要在Action元素中添加interceptor-ref节点来指定使用哪个拦截器,代码示例如下:
<?xml version="1.0" encoding="UTF-8" ?><!-- XML声明 --> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"><!-- 指定Struts 2配置文件的DTD信息 --> <struts><!-- 根节点 --> <package name="struts2" extends="struts-default"> <interceptors> <interceptor name="myInterceptor" class="com.sanqing.interceptor.MyInterceptor2"></interceptor> </interceptors> <action name="register" class="com.sanqing.action.RegisterAction"> <result name="success">/ShowUserInfo.jsp</result> <interceptor-refname="myInterceptor"></interceptor-ref> </action> </package> </struts>
2.5 使用默认拦截器
Struts 2里面的Action在没有配置拦截器时,实际上还是有默认的拦截器在工作,默认情况下是使用defaultStack这个拦截器栈,一旦为某个Action添加了自定义拦截器,就必须为该Action配置默认拦截器栈defaultStack,一般是在<action>节点中通过<interceptor-ref>节点引用默认拦截器栈,通常将defaultStack配置在自定义拦截器之后,这样就能保证业务控制器首先被自定义拦截器拦截,配置代码如下:
<action name="register" class="com.sanqing.action.RegisterAction"> <result name="success">/ShowUserInfo.jsp</result> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action>
第三节 深入了解拦截器
3.1 为拦截器传入参数
可以通过两种方式为拦截器传入参数,一种是在配置拦截器时配置参数,一种是在使用拦截器时配置参数。要为拦截器传入参数,拦截器就必须能接受参数,下面来定义一个拦截器,能够接受参数,并对参数进行处理:
package com.sanqing.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.interceptor.Interceptor; public class MyInterceptor3 extends AbstractInterceptor{ private String interceptorName; //名称属性 public void setInterceptorName(String interceptorName) {//设置名称属性 this.interceptorName = interceptorName; } public String intercept(ActionInvocation invocation) throws Exception { System.out.println(interceptorName + ":拦截前操作"); //拦截前执行操作 String result = invocation.invoke(); //向下传递 System.out.println(interceptorName + ":拦截后操作"); //拦截后执行操作 return result; //返回结果 } }
(1)在配置拦截器时配置参数
<interceptor name="myInterceptor3" class="com.sanqing.interceptor.MyInterceptor3"> <param name="interceptorName">配置拦截器时配置参数</param> </interceptor>
(2)在使用拦截器时配置参数
<action name="register" class="com.sanqing.action.RegisterAction"> <result name="success">/ShowUserInfo.jsp</result> <interceptor-ref name="myInterceptor3"> <param name="interceptorName">使用拦截器时配置参数</param> </interceptor-ref> </action>
注:在配置和使用拦截器时都可以为其配置参数,但是如果两者都使用的话,后者会将前者的参数值给覆盖,推荐使用在配置拦截器时配置参数,这样配置的参数是公用的,在所有Action中的使用只需配置一次,至于在使用拦截器时配置参数,只有在某些个别情况下使用拦截器时再为其配置参数,从而覆盖前面配置的默认值。
3.2 配置拦截器栈
(1)下面这段代码主要演示如何配置拦截器栈,并在业务控制器中使用拦截器栈
<interceptors> <interceptor name="myInterceptor2" class="com.sanqing.interceptor.MyInterceptor2"></interceptor> <interceptor name="myInterceptor3" class="com.sanqing.interceptor.MyInterceptor3"></interceptor> <interceptor-stack name="myInterceptorStack"> <interceptor-ref name="myInterceptor2"></interceptor-ref> <interceptor-ref name="myInterceptor3"></interceptor-ref> </interceptor-stack> </interceptors> <action name="register" class="com.sanqing.action.RegisterAction"> <result name="success">/ShowUserInfo.jsp</result> <interceptor-ref name="myInterceptorStack"></interceptor-ref> </action>
(2)在前面的配置中,我们一般都是在<action>节点中单独引入Struts 2的默认拦截器栈defaultStack,其实完全可以将其加入到自定义的拦截器栈中,将默认拦截器栈defaultStack加入到自定义的拦截器栈中,使用起来更加方便,不用再为多个Action重复配置该默认拦截器栈,配置代码如下:
<interceptor-stack name="myInterceptorStack"> <interceptor-ref name="myInterceptor2"></interceptor-ref> <interceptor-ref name="myInterceptor3"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack>
(3)在拦截器栈中引入拦截器时,可以为拦截器配置参数,配置代码如下:
<interceptor-stack name="myInterceptorStack"> <interceptor-ref name="myInterceptor2"></interceptor-ref> <interceptor-ref name="myInterceptor3"> <param name="interceptorName">引用时配置参数</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack>
3.3 覆盖拦截器栈中指定拦截器的参数
前面介绍了在使用拦截器时可以为拦截器指定参数,同样在使用拦截器栈时也可以为拦截器指定参数,从而覆盖在配置拦截器栈时指定的参数值,在Action节点下的interceptor-ref节点中通过添加param节点来添加参数,并指定参数名和参数值,代码如下所示:
<action name="register" class="com.sanqing.action.RegisterAction"> <result name="success">/ShowUserInfo.jsp</result> <interceptor-ref name="myInterceptorStack"> <param name="myInterceptor3.interceptorName">使用拦截器栈</param> </interceptor-ref> </action>
3.4 拦截器执行顺序
拦截器执行顺序规则如下:
(1)在拦截器中先引入的拦截器会先执行,后引入的拦截器会后执行;
(2)一个拦截器执行完其拦截前操作后,会传递给下一个拦截器(如果有多个拦截器的话)或者Action;
(3)当一个拦截器的拦截后操作执行完后,会返回其上一个拦截器,继续执行上一个拦截器的拦截后操作。
根据第2点和第3点规则绘制的拦截器执行流程图如下图所示:
第四节 Struts 2内建拦截器
4.1 内建拦截器介绍
4.2 内建拦截器的配置
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?