1. 拦截器简介
拦截器是struts2中的一大特色,在调用action前,执行许多struts默认使用的拦截器,这些拦截器实现了许多基本的功能,让程序员编程更加轻松
2. 为什么拦截器
OOP就是面相对象编程,AOP就是面相切面编程,而拦截器就是AOP,初次使用也不太理解其真正意义.....待日后补全
3. 如何使用拦截器
在使用前,先了解一下工作拦截器的工作原理,ActionInvocation类负责整个Action的执行,它除了包含Action对象,Action的一些运行时的一些相关信息,如请求对象,返回Result等等,还包含拦截器的引用。当Stuts框架收到url的请求后,实例化一个Action对象,并赋给一个新的ActionInvocation对象,然后根据配置文件把这个Action使用到的拦截器赋给这个ActionInvocation.
当正式运行的时候,ActionInvocation的InVoke()方法会被调用,这个方法里面会先执行所有的拦截器的intercept()方法,当最后一个拦截器被调用完毕后才执行Action。
最后执行完action后,才逐一把刚才调用的拦截器释放。这里到底是怎样完成的呢?
根据下面的图片进行分析,(最后可以找段源码来自己阅读一下,会更清晰)
- 先执行ActionInvocaton里的inVoke()方法,然后遍历拦截器列表,
- 调用拦截器的intercept()方法,在intercept()方法中递归调用ActionInvocation的invoke()方法,
- 如此类推,直到最后一个拦截器被调用完毕后,会立即执行Action部分,
- Action部分执行完成后,回到对上一个拦截器中,完成intercept()方法中未完成的代码部分,因为上action部分已经被完成,所以在这个拦截器的代码完成后,Action部分不会再被执行,
- 如此类推继续上一个拦截器,直到第一个被加入的拦截器被释放,所以拦截器是先进后出的机制。
拦截器在strut.xml里面实现并注入,那个action使用了何种拦截器,都体现在配置文件当中,一把情况我们的package会继承struts-default,这里继承了struts默认的拦截器。
<struts> <package name="default" extends="struts-default"> <action name="go" class="cn.com.test.actions.Submit" method="go"> <result name="success">page2.jsp</result> </action> </package> </struts>
一下是从某书当中截取的拦截器说明列表
如果我们想单独调用这些拦截器,我们需要在配置文件action中加入拦截器字段interceptor-ref,在这里我们需要注意的是,当我们手动添加了拦截器后,默认的拦截器就不会被加载,所以记得,如果我们是手动添加了拦截器并想使用原有默认的拦截器必须显式添加默认拦截器栈,图中defaultStack为默认拦截器栈。
4. 自定义拦截器
这里有两个注意的地方,第一个是配置文件struts.xml,拦截器必须写在action的前面,因为配置文件的读取是顺序读取,如果写在后面那么action定义的拦截器就取不到定义的拦截器了,
它的定义方式与action类似,有一个name作为拦截器的标识名称,class为实现类,在用到这个拦截器的action里面写上interceptor-ref字段,把使用到的拦截器名称写上即可。
第二个问题就是拦截器实现类,这个实现类写法有两种,一种是实现com.opensymphony.xwork2.interceptor.Interceptor接口,一种是实现,另外一种是继承com.opensymphony.xwork2.interceptor.AbstractInterceptor类,后者会更受欢迎,因为前者需要init()和destroy()方法,需然从代码上看,AbstractInterceptor并没有具体代码实现这两个方法的内容,以下是一个例子:
这里需要主要的有两个,
第一个是第10行,拦截器唯一能获取的参数是这个ActionInvocation,不要少看这个ActionInvocation,它能获取几乎整个回话当中的内容,包括访问参数等等。以下是某说给出的一个表,涵盖了ActionInvocation的常用方法
然后代码中就进行了分析,假如获取不到用户信息,则返回一个重新登录的结果。
第二个是第25行,当不需要拦截的时候,请把控制权交回Invocation,请把控制权交回Invocation,请把控制权交回Invocation,重要的事情说三遍,交回invcation继续查看下一个拦截器。
直到所有拦截器都完成操作并完成Action部分,这样拦截器就会逐一释放
1 public class LoginInter extends AbstractInterceptor { 2 3 /** 4 * 5 */ 6 private static final long serialVersionUID = 1L; 7 8 @Override 9 public String intercept(ActionInvocation invocation) throws Exception { 10 Map session = invocation.getInvocationContext().getSession(); 11 User user = (User) session.get("user"); 12 if (user == null) { 13 // 返回拦截的结果 14 return Action.LOGIN; 15 } else { 16 Action action = (Action) invocation.getAction(); 17 if (action instanceof HomeAction) { 18 ((HomeAction) action).setUser(user); 19 } 20 21 System.out.println("Login before"); 22 /* 23 * 把控制权返还给invocation,继续执行下一个拦截,直到没有拦截器后会执行action部分 24 */ 25 String result = invocation.invoke(); 26 System.out.println("Login after"); 27 28 return result; 29 } 30 31 } 32 33 }
5. 拦截器与返回结果
根据上面的例子,有人会有疑问,如果action执行完成后,则代码会返回一个结果,这个结果会返回到上面的代码的第25行继续往下跑,在这个时候,是否能修改其返回结果(result),或者继续对action内的参数进行修改呢?答案是否定的,以下的代码是修改后的代码,添加了第28-32行,在这段代码当中,试图修改其action内的值,和result的返回值,但运行结果发现并没有对返回的页面结果有任何的影响。这说明,当action执行完后,结果会立即返回。
1 public class LoginInter extends AbstractInterceptor { 2 3 /** 4 * 5 */ 6 private static final long serialVersionUID = 1L; 7 8 @Override 9 public String intercept(ActionInvocation invocation) throws Exception { 10 Map session = invocation.getInvocationContext().getSession(); 11 User user = (User) session.get("user"); 12 if (user == null) { 13 // 返回拦截的结果 14 return Action.LOGIN; 15 } else { 16 Action action = (Action) invocation.getAction(); 17 if (action instanceof HomeAction) { 18 ((HomeAction) action).setUser(user); 19 } 20 21 System.out.println("Login before"); 22 /* 23 * 把控制权返还给invocation,继续执行下一个拦截,直到没有拦截器后会执行action部分 24 */ 25 String result = invocation.invoke(); 26 System.out.println("Login after"); 27 28 result = Action.LOGIN; 29 Action action2 = (Action) invocation.getAction(); 30 if (action2 instanceof HomeAction) { 31 ((HomeAction) action2).setPromote("I hava changed the result"); 32 } 33 34 35 return result; 36 } 37 38 }
39}
6. 监听拦截器
在返回action页面结果的时候是否就不能对其页面进行一些修改呢?答案是否定的。struts已经为这安排了一个监听器,就是在action将要返回结果的时候,插入了一个监听器com.opensymphony.xwork2.interceptor.PreResultListener。我们可以在拦截器中测试一下。在上面的代码当中,添加一个监听器。添加代码部分是11-22行。在这个监听里面我们修改了action的其中一个参数,结果发现,修改成功。
1 public class LoginInter extends AbstractInterceptor { 2 3 /** 4 * 5 */ 6 private static final long serialVersionUID = 1L; 7 8 @Override 9 public String intercept(ActionInvocation invocation) throws Exception { 10 Map session = invocation.getInvocationContext().getSession(); 11 invocation.addPreResultListener(new PreResultListener() { 12 13 @Override 14 public void beforeResult(ActionInvocation invocation, 15 String resultCode) { 16 Action action = (Action) invocation.getAction(); 17 if (action instanceof HomeAction) { 18 ((HomeAction) action).setPromote("I hava changed the result by listener"); 19 } 20 21 } 22 }); 23 24 User user = (User) session.get("user"); 25 if (user == null) { 26 // 返回拦截的结果 27 return Action.LOGIN; 28 } else { 29 Action action = (Action) invocation.getAction(); 30 if (action instanceof HomeAction) { 31 ((HomeAction) action).setUser(user); 32 } 33 34 System.out.println("Login before"); 35 /* 36 * 把控制权返还给invocation,继续执行下一个拦截,直到没有拦截器后会执行action部分 37 */ 38 String result = invocation.invoke(); 39 System.out.println("Login after"); 40 41 42 Action action2 = (Action) invocation.getAction(); 43 if (action2 instanceof HomeAction) { 44 ((HomeAction) action2).setPromote("I hava changed the result"); 45 } 46 47 48 return result; 49 } 50 51 } 52 53 }
7. 拦截器的执行顺序和参数传递
最后我们来看一下,拦截器的执行顺序和他是如何传参的。首先我们来看看例子,在配置里面struts.xml,添加了两个拦截器,都是同一个拦截器,struts是允许同时加载相同的拦截器的。
<struts> <package name="default" extends="struts-default"> <interceptors> <interceptor name="Login" class="cn.com.test.interceptor.LoginInter"></interceptor> </interceptors> <action name="go" class="cn.com.test.actions.Submit" method="go"> <result name="input">/Login.jsp</result> <result name="success">/page2.jsp</result> </action> <action name="home" class="cn.com.test.actions.HomeAction"> <result name="success">/home.jsp</result> <result name="login">/Login.jsp</result> <interceptor-ref name="Login"> <param name="name">第一个拦截器</param> </interceptor-ref> <interceptor-ref name="Login"> <param name="name">第二个拦截器</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package> </struts>
然后在拦截器的实现类,在这个类可以看到有一个name的setter/getter,这个name对应的就是上面配置文件中,拦截器的参数name
public class LoginInter extends AbstractInterceptor { private String name; /** * */ private static final long serialVersionUID = 1L; @Override public String intercept(ActionInvocation invocation) throws Exception {
System.out.println(name+"正在进入"); String result = invocation.invoke(); System.out.println(name+"正在退出");return result; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
这里是返回的结果,从结果可以看出,拦截器是先进后出,
执行第一个拦截器,执行第二个拦截器,然后执行action里面的内容(action部分的代码省略),然后第二个拦截器退出,最后第一个拦截器退出