与Spring类似,Struts2也有拦截器Interceptor,拦截器是java动态拦截action请求的对象,其可以复用,扩展,适合封装通用的处理。下面将学习拦截器作用的时机,以及如何使用拦截器,做简单的记录。
拦截器作用时机
浏览器发送请求到服务端,正常会经历如下几步:浏览器发送请求→filter处理→action处理→result→返回浏览器。
加上拦截器后的过程:浏览器发送请求→filter处理→拦截器处理→action处理→拦截器处理→result→返回浏览器。
也就是说正常的访问过程,浏览器发送请求后,filter先进行处理,并读取struts.xml配置文件,进行action具体路径的匹配并处理,最后将结果返回给浏览器。如果加上拦截器,相当如filter接受到请求到转发给具体action处理前,先调用拦截器进行处理,处理完成后再交给action进行处理,然后action处理完请求后,拦截器再进行一次拦截处理,最后再将结果返回给result进行处理,返回给浏览器。
拦截器的使用
拦截器的使用基本就是三步:创建拦截器,注册拦截器,引用拦截器,接下来以一个简单的案例,说明如何使用拦截器。案例在SSH02的基础上进行,基本的Struts2+Spring的配置省略。
(1)创建拦截器,需要创建一个拦截器类,并实现Interceptor接口,其中invocation.invoke()方法起到放行的作用,如果拦截器逻辑执行完能到这里,就将执行接下来的action请求对应的逻辑。
package Controller; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; /** * 自定义拦截器,实现简单的拦截*/ @Controller("demoInterceptor") @Scope("prototype") public class demoInterceptor implements Interceptor{ public void destroy() { System.out.println("拦截器销毁了"); } public void init() { System.out.println("拦截器初始化了"); } //拦截逻辑写在这里 public String intercept(ActionInvocation invocation) throws Exception { System.out.println("开始拦截"); invocation.invoke();//action请求放行 System.out.println("结束拦截"); return null; } }
(2)注册拦截器
在package标签下,action标签前,注册拦截器,其中可以注册多个拦截器,name属性对应注册的拦截器名,class属性对应拦截器处理类。其中myInterceptor就是这次注册的拦截器名,其实现类为上面创建的demoInterceptor。
<package name="demo" namespace="/hello" extends="struts-default"> <!-- 添加自定义拦截器 --> <!-- 注册拦截器 --> <interceptors> <interceptor name="myInterceptor" class="demoInterceptor"></interceptor> </interceptors> <action name="helloStruts" class="helloStruts"> <result name="success"> /WEB-INF/helloStruts.jsp </result> </action> </package>
(3)引用拦截器
在action标签下,引用注册的拦截器,name属性值就是注册的拦截器名称。其中name属性值就是上面注册的拦截器名myInterceptor。补充引用拦截器配置后,上面的配置变成如下。
<package name="demo" namespace="/hello" extends="struts-default"> <!-- 添加自定义拦截器 --> <!-- 注册拦截器 --> <interceptors> <interceptor name="myInterceptor" class="demoInterceptor"></interceptor> </interceptors> <action name="helloStruts" class="helloStruts"> <result name="success"> /WEB-INF/helloStruts.jsp </result> <!-- 引用拦截器 --> <interceptor-ref name="myInterceptor"></interceptor-ref> </action> </package>
部署测试结果
Interceptor初始化
在action逻辑处理前后执行了开始拦截和结束拦截
浏览器输出结果
测试结果说明拦截器按照上面说的正常执行了。
拦截器栈失效问题
如果不配置拦截器,框架会默认调用自己的拦截器,其在struts-default.xml中有配置,然后消息传递的数据也是拦截器进行处理的。如果引用了自己定义的拦截器,按照如上配置的话,系统默认的拦截器栈将撤销,导致部分功能不可以用,如消息传递机制失效,下面使用SSH01中的登录功能,配置自定义拦截器进行测试。
(1)struts-default.xml中定义的拦截器栈
(2)在登录验证中配置拦截器
<!-- 登录,提交用户名和密码,采用基本属性提交方式 --> <package name="login" namespace="/login" extends="struts-default"> <!-- 注册自定义拦截器,消息传递机制失效测试 --> <interceptors> <interceptor name="myInterceptor" class="com.boe.Filter.demoInterceptor"></interceptor> </interceptors> <!-- 进入登录页面,没有业务逻辑处理 --> <action name="toLogin"> <result name="success"> /WEB-INF/login.jsp </result> </action> <!-- 验证用户名和密码 --> <action name="login" class="com.boe.Filter.Login"> <result name="success"> /WEB-INF/msg.jsp </result> <!-- 引入自定义拦截器 --> <interceptor-ref name="myInterceptor"></interceptor-ref> </action> </package>
(3)登录验证
测试发现,action方法中打印用户名和密码得到的结果都为null,说明没有获取到浏览器发送的数据。
拦截器栈失效问题解决方法
如果想同时实现系统默认的拦截器,以及自己定义的拦截器,需要定义一个拦截器栈,里面引入系统默认的拦截器栈,以及自己定义的拦截器。下面在域模型传递方式中,配置拦截器栈,并测试。
(1)在登录中配置拦截器栈
<!-- 登录,使用域模型传递方式 --> <package name="loginWithEntity" namespace="/loginWithEntity" extends="struts-default"> <!-- 注册拦截器,并创建interceptor-stack,既导入自己写的拦截器也导入框架默认的拦截器栈 --> <interceptors> <!-- 自定义拦截器 --> <interceptor name="myInterceptor" class="com.boe.Filter.demoInterceptor"></interceptor> <interceptor-stack name="myInterceptorStack"> <!-- 引入自己定义的拦截器和框架默认的 --> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 进入登录页面 --> <action name="toLogin"> <result name="success"> /WEB-INF/loginWithEntity.jsp </result> </action> <!-- 登录验证 --> <action name="login" class="com.boe.Filter.LoginWithEntity"> <result name="success"> /WEB-INF/msg.jsp </result> <!-- 引入自己定义的拦截器栈 --> <interceptor-ref name="myInterceptorStack"></interceptor-ref> </action> </package>
(2)登录验证
测试发现配置自定义拦截器栈,并引入自定义的拦截器,以及框架默认的拦截器栈后,数据传递机制生效。说明框架默认的拦截器有获取数据的功能。其中自定义拦截器栈和拦截器标签名是同级关系。
结论
(1)Struts2的拦截器会作用于action请求开始前和结束后,其可以复用,扩展。
(2)拦截器的配置按照三步走:创建拦截器,注册拦截器和引入拦截器。
(3)配置自定义拦截器会有导致框架默认拦截器栈功能撤销的可能,如果想同时实现自定义拦截器和框架默认拦截器栈的功能,需将两者配置到自定义拦截器栈中,并在action引入后才能都生效。