6.4 两个有用的拦截器
6.4.1 更强大的logger拦截器
在学习了这么多拦截器的理论知识过后,来示范两个真实项目中用得到的拦截器。先来看看更强大的logger拦截器。
Struts2自带的logger拦截器只是打印出了Action所对应的URL以及执行的方法名称,这对实际开发来说是肯定不够的。
实际开发中为了调试方便,要记录的信息比较多,通常需要把这次请求相关的几乎所有信息都打印出来,比如:
- 要访问哪个Action类
- 要访问这个Action类的哪个方法
- 打印出这次请求中所有的request中的parameter参数
- 这次请求最后跳转到哪个页面。
如果我们现在就要在拦截器中实现这样的功能,该怎么实现呢?
1:ActionInvocation接口
在实现拦截器的功能的时候,需要使用ActionInvocation接口,这个接口有很多的功能,这里并不打算全部讲到,只描述一下接下来我们要用到的功能,更多的功能请参见Struts2的API文档。
- getAction方法:返回这次请求准备执行的Action对象。
- getProxy方法:返回这次请求的ActionProxy对象,可以在这个对象上获得要运行Action的哪个方法。
- getInvocationContext方法:返回这个Action执行的上下文(ActionContext),可以在这个上下文对象中获取到大量的数据,比如请求的parameter值、session的值等等。
在ActionContext中取到的parameter值是一个Map<String,Object>,其中以String为key,以String[]为value。这个Map中记录了所有的request参数。 - getResult方法:返回Result运行之后代表结果的Result对象。
2:具体的LoggerInterceptor,示例代码如下:
- package cn.javass.action.action;
- import java.util.Map;
- import org.apache.struts2.dispatcher.ServletDispatcherResult;
- import com.opensymphony.xwork2.ActionInvocation;
- import com.opensymphony.xwork2.Result;
- import com.opensymphony.xwork2.interceptor.Interceptor;
- public class LoggerInterceptor implements Interceptor{
- public void destroy() {
- }
- public void init() {
- }
- public String intercept(ActionInvocation invocation) throws Exception {
- System.out.println("begin-------------------------------");
- //找到运行的Action对象,并打印其类名
- System.out.println("Action:"+invocation.getAction().getClass().getName());
- //找到运行的ActionProxy对象,并打印其要运行的方法名
- System.out.println("Method:"+invocation.getProxy().getMethod());
- //找到这次请求的request中的parameter参数,并打印
- Map<String, Object> params = invocation.getInvocationContext().getParameters();
- for (String key:params.keySet()){
- Object obj = params.get(key);
- if(obj instanceof String[]){
- String[] arr = (String[]) obj;
- System.out.println("Param:"+key);
- for (String value:arr){
- System.out.println(value);
- }
- }
- }
- //运行后续的拦截器、Action和Result
- String resultCode = invocation.invoke();
- //在Action和Result运行之后,得到Result对象
- //并且可以强制转换成ServletDispatcherResult,打印其下一个JSP的位置
- Result rresult = invocation.getResult();
- if (rresult instanceof ServletDispatcherResult){
- ServletDispatcherResult result = (ServletDispatcherResult) rresult;
- System.out.println("JSP:"+result.getLastFinalLocation());
- }
- System.out.println("end-------------------------------"); return resultCode;
- }
- }
3:在struts.xml中配置和使用这个拦截器,示例如下:
- <package name="helloworld" extends="struts-default">
- <interceptors>
- <interceptor name="MyLogger" class="cn.javass.action.action.LoggerInterceptor"/>
- </interceptors>
- <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">
- <result name="toWelcome">/s2impl/welcome.jsp</result>
- <interceptor-ref name="MyLogger"/>
- <interceptor-ref name="defaultStack"/>
- </action>
- </package>
4:测试一下,运行登录页面,填入用户名和密码,点击提交按钮。然后查看后台的输出,示例如下:
- begin-------------------------------
- Action:cn.javass.action.action.HelloWorldAction
- Method:execute
- Param:submitFlag
- login
- Param:account
- test
- Param:password
- test
- 用户输入的参数为===account=test,password=test,submitFlag=login
- JSP:/s2impl/welcome.jsp
- end-------------------------------
上面加粗的那句话,是Action运行的时候输出的信息,其余信息就是由我们的拦截器来输出的了。
基本实现了前面提出的四个要求,分别输出了如下信息:运行哪个Action类,运行哪个方法,请求的参数,Action运行完要跳转到哪个JSP。
这些信息对我们进行调试是非常有用的。
6.4.2 登录检查拦截器
在实际开发中,一个常见的功能要求是:有很多操作都需要登录后才能操作,如果操作的时候还没有登录,那么通常情况下会要求跳转回到登录页面。
1:如何实现这样的功能呢?
在具体实现之前,先来考虑几个问题:
(1)这个功能应该在哪里实现?
要实现登录检查的功能,很明显是在Action运行之前,就要判断用户是否登陆了,判断方式是看session里面是否有相关信息,如果有,则继续操作;如果没有,则要跳转到预先制定好的登录页面。
简单点说,登录检查应该写在“invocation.invoke();”语句之前。
(2)是否需要参数?
要判断是否需要参数,其实就是判断这个拦截器有没有什么可变的量,可以把这些可变量从程序中分离出来,通过struts.xml来配置。经过分析,可以抽出两个参数:
- 代表登陆页面的Result
- 判断session中哪个attribute,也就是attribute的名称
(3)如何引用呢?
现在的情况是只有部分Action需要登陆录检查,另外一些Action不需要,这就需要权衡了。对于大多数Action都要进行登录检查的包,可以在包的默认拦截器引用上设置登录检查,而对于少数不需要登陆检查的Action,可以让它们直接引用默认的defaultStack拦截器栈。
2:接下来,就来实现满足要求的拦截器,示例代码如下:
- package cn.javass.action.action;
- import java.util.Map;
- import com.opensymphony.xwork2.ActionContext;
- import com.opensymphony.xwork2.ActionInvocation;
- import com.opensymphony.xwork2.interceptor.Interceptor;
- public class SessionCheckInterceptor implements Interceptor{
- //设置参数
- private String sessionAttribute;
- private String reloginResult;
- public void setSessionAttribute(String sessionAttribute) {
- this.sessionAttribute = sessionAttribute;
- }
- public void setReloginResult(String reloginResult) {
- this.reloginResult = reloginResult;
- }
- public void destroy() { }
- public void init() { }
- public String intercept(ActionInvocation invocation) throws Exception {
- //读取Session
- Map<String, Object> session = invocation.getInvocationContext().getSession();
- //判断Session中是否有相应的attribute
- if (session.containsKey(sessionAttribute)){
- String resultCode = invocation.invoke();
- return resultCode;
- }else{
- return reloginResult;
- }
- }
- }
在intercept方法中,先读取session中指定的attribute,具体读取哪个attribute由参数从外界传进来,然后判断Session中是否存在这个attribute,如果有则继续执行后续的拦截器、Action和Result,如果没有则跳转到指定的Result所对应的页面,具体跳转到哪个Result也是由参数从外界传进来的。
3:写好了拦截器,该来写配置了,配置示例如下:
- <package name="helloworld" extends="struts-default">
- <interceptors>
- <interceptor name="LoginChecker" class="cn.javass.action.action.SessionCheckInterceptor"/>
- <interceptor-stack name="myStack">
- <interceptor-ref name="LoginChecker">
- <param name="sessionAttribute">login_user</param>
- <param name="reloginResult">login</param>
- </interceptor-ref>
- <interceptor-ref name="defaultStack"/>
- </interceptor-stack>
- </interceptors>
- <default-interceptor-ref name="myStack"/>
- <global-results>
- <result name="login">/login.jsp</result>
- </global-results>
- <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">
- <result name="toWelcome">/s2impl/welcome.jsp</result>
- </action>
- <action name="secondAction" class="cn.javass.action.action.SecondAction">
- <result name="toWelcome">/s2impl/welcome.jsp</result>
- <interceptor-ref name="defaultStack"/>
- </action>
- </package>
配置比较多,分解说明一下:
(1)上面的配置声明了一个叫LoginChecker的自定义拦截器,如下:
- <interceptor name="LoginChecker" class="cn.javass.action.action.SessionCheckInterceptor"/>
2)声明了一个myStack拦截器栈,在这个栈里面引用了LoginChecker拦截器和defaultStack拦截器,在引用LoginChecker拦截器的时候,还传入了两个自定义的参数,其中sessionAttribute传入login_user,reloginResult传入login,以备在拦截器中使用,如下:
- <interceptor-stack name="myStack">
- <interceptor-ref name="LoginChecker">
- <param name="sessionAttribute">login_user</param>
- <param name="reloginResult">login</param>
- </interceptor-ref>
- <interceptor-ref name="defaultStack"/>
- </interceptor-stack>
(3)将myStack拦截器栈指定为这个包的默认拦截器引用,如下:
- <default-interceptor-ref name="myStack"/>
(4)声明了一个全局Result,作为所有需要登录检查的Action在没有登陆的时候,跳转到的登陆页面。这个Result的命名,要与引用LoginChecker拦截器时注入的reloginResult参数一致,如下:
- <global-results>
- <result name="login">/login.jsp</result>
- </global-results>
(5)这时,在整个包中,没有自己声明<interceptor-ref>的Action,都会采用包声明的默认拦截器引用,比如已经可以对helloworldAction执行登录检查了。
为了测试需要,对于secondAction,配置了引用defaultStack,这样它就不会进行登录检查。
(6)去测试看看,是否能满足功能要求。
私塾在线网站原创《研磨struts2》系列
转自请注明出处:【http://sishuok.com/forum/blogPost/list/0/4060.html】
欢迎访问http://sishuok.com获取更多内容