拦截器初探
昨天临睡觉之前看了看拦截器,也在昨天的学习笔记里面胡诌诌了几句,今天就来好好的会会拦截器这个东西。实际上拦截器是一种模块实现的机制<起码我是这么体会的>(至于说书里面说体现了AOP的设计思想…我连AOP是啥还没搞清楚好伐…),在struts1的版本里面,struts将所有的功能围绕着核心控制器来实现的,而到了Struts2,控制器不在承担过多的功能支持,将这些功能拓展交给拦截器来完成。
拦截器设计为在action之前或action之后执行,从系统的架构图也能看出来,除了系统核心的拦截器外,你还可以插入其他的拦截器。
拦截器还有一个好处,就是解耦合。比如说A方法中需要验证username长度不能超过25位,B方法也需要,C方法也需要,那么我们就可以考虑将这个验证放在一个拦截器中实现,在执行Action之前执行,如果错误就返回,如果正确继续执行Action。
那么到底拦截器的原理是什么呢?我觉得从struts的书上是找不到答案了,度娘帮一下,下面是度娘给的定义…
java里的拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。在AOP(Aspect-Oriented Programming)中拦截器用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。
这么一查的倒是提醒我了,有一点很重要,拦截器并非什么特殊的代码,他只是一个Java类,实现起来很简单。还有就是拦截器与过滤器的区别:
过滤器可以简单理解为
取你所想取
忽视掉那些你不想要的东西;拦截器可以简单理解为
拒你所想拒
关心你想要拒绝掉哪些东西,比如一个BBS论坛上拦截掉敏感词汇。
1.拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2.过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3.拦截器只对action起作用,而过滤器几乎可以对所有请求起作用。
4.拦截器可以访问action上下文、值栈里的对象,而过滤器不能。
5.在action的生命周期里,拦截器可以多起调用,而过滤器只能在容器初始化时调用一次。
现在发现和自己的想法差不多,核心一句话“拒你所相拒”!
声明和使用拦截器
搞清楚概念和用途后,就该尝试一下了。我一直很讨厌书大而全,啥都给你讲,生怕你不知道乾隆的第42个娘娘的外婆的外甥女的爸爸叫啥,有必要么?我们需要将最常用的东西,其他就直接说有几种方法,其中哪两种已经不适用了,我们介绍这种,就OK了,唉….
现在开发实际上都是直接在struts.xml配置拦截器,这样就避免了在类中指定的高耦合的情况。在Struts.xml配置过滤器的格式是:
<interceptor name=”interceptorName”class=”interceptorClass /></interceptor>
实际上定义的格式与Action十分类似。如果你像向配置的拦截器传递参数你还可以在标签之中添加param标签,指定你需要传递的参数即可。
在开发中往往需要多个拦截器配合使用,这时候需要一个拦截器栈了。他的定义就是
<interceptor-stack name=”xxx”></interceptor-stack>在标签里面使用
<interceptor-ref>标签来指定拦截器。
具体怎么使用通过例子来演示吧,没有例子干看配置,总也不会太明白的,也没兴趣。
当然还有几个概念:
默认拦截器
<default-interceptor-ref name=”已经存在的拦截器或拦截器栈的名字”/>这个标签是package的子标签。
内建拦截器
我们没有配置拦截器的时候,你就已经可以获取到HHTP请求的传值了,这就是因为内建拦截器把参数拿到了存到了你可以拿到的地方。
内建拦截器就像一个工具库一样,为你提供了很多使用的东西,比如说提供文件上传功能的fileUpload啊,比如说输出Action执行时间的Timer等等等,这些内建拦截器用到了去查就行了。
下面看一看怎样自定义一个拦截器来扩展struts的功能:
首先需要实现Interceptor接口,需要实现三个方法,init、destory和interceptor。那么Struts2的AbstractInterceptor已经为我们实现了Interceptor接口,大多数情况下我们直接继承AbstractInterceptor就可以了,这个和Action接口与ActionSupport类的关系很相似。
下面是一个拦截器的实现:
package tech.youngs.Interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;public class RegInterceptor extends AbstractInterceptor{
private static final long serialVersionUID = 1L;@Override
public String intercept(ActionInvocation arg0) throws Exception {
// TODO Auto-generated method stub
System.out.println("拦截器开始工作");
String result = arg0.invoke();
System.out.println("拦截器结束工作");
return result;
}}
Action的代码其实没什么特别的就不贴了,这里贴出struts.xml的配置代码:
<interceptors>
<interceptor name="register" class="tech.youngs.Interceptor.RegInterceptor"/>
</interceptors>
<action name="reg" class="tech.youngs.Action.RegisterAction">
<result name="success">/WEB-INF/jsp/success.jsp</result>
<result name="input">/WEB-INF/jsp/reg.jsp</result>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="register" />
</action>
特别注意 是<interceptors>标签里面定义着拦截器 在action里面使用<interceptor-ref>引用就行了。在写代码的时候还出现了一个错误,就是引用的定义的拦截器的name不相同了,给我报404的页面,现在体会着struts报了404绝壁是配置文件除了问题,慢慢找就行了 注意一下什么name想不想同啊 这一类的问题。
拦截器深入
上面只介绍了拦截器的原理,还不知道内部是怎么运行的,输出了一下args.invoke 控制台显示了input也就是不成功的时候的字符串。这个东西我觉得网上的文章说的很好:
invocation.invoke() 就是通知struts2接着干下面的事情
比如 调用下一个拦截器 或 执行下一个Action
就等于退出了你自己编写的这个interceptor了
1. 如果拦截器堆栈中还有其他的Interceptor,那么invocation.invoke()将调用堆栈中下一个Interceptor的执行。
2. 如果拦截器堆栈中只有Action了,那么invocation.invoke()将调用Action执行。
特别注意,在使用自定义拦截器的时候,必须引用struts2自带的拦截器缺省堆栈defaultStack,不然会出错。
在实际的开发过程中,实际很少会对整个Action去拦截,可能只需要对某几个方法去进行验证之类的工作,那么struts提供了一种过滤方法的拦截器,这个拦截器与普通拦截器的唯一不同就是,继承了MethodFilterIntercepter,这个类已经继承了AbstractInterceptor,它重写了intercept方法,还提供了doIntercept的抽象方法供用户实现。
其实MethodFilterIntercepter与普通的拦截器的实现代码没什么区别。那么怎么实现过滤到需要拦截的方法呢——struts.xml继续看….
<intercepter-ref name=”xxxInterceptor”>
<param name=”executeMethods”>execute,login</param>
</intercepter-ref>
在引用自定义拦截器的时候,增加一个名为executeMethods的参数就行了,中间的方法名将不会被拦截,特别注意是executeMethods,这个在声明拦截器的时候的坑很像,就是复数,而且这种错误犯了的话会很难找的,interceptors和executeMethods。中间的方法名使用都好隔开就行了。
下面看一个比较高级的例子吧,权限控制。这个我在PHP的时候做过,思路就是库内的用户权限有单独的字段,然后他登录了权限字段会存入Session中,那么页面会判断你这个session里的权限是不是满足访问页面的最低权限,ok就进,or就出,就是这样的思路。
这里记一下ActionContext的几种获取方式:
- 在自定义的拦截器中:使用ActionInvocation.getInvocationContext()或者使用ActionContext.getContext()。
- 在Action类中:让拦截器注入或者使用ActionContext.getContext()。
- 在非Action类中:让Action类传递参数、使用注入机制注入或者使用ActionContext.getContext()。注意:只有运行在request线程中的代码才能调用ActionContext.getContext(),否则返回的是null。
在上面我们看到,在自定义的拦截器中两种方式实际上是对等的。
在MVC框架里面有这样一条开发的原则,尽量不要再Controller层做过多的判断,这真的是一条比较好的法则,希望各位小伙伴们在开发的时候遵循。
那么在Struts2内也是一样的,我们不要讲权限的判断交给Action去执行,我们将权限的控制放在拦截器中去实现:
…..我都郁闷了 弄了一下午sessionMap没有值…..
求大神解答 为啥ActionContext在interceptor里面没有值呢?