一、简介
拦截器体系是struts2重要的组成部分。正是大量的内建拦截器完成了该框架的大部分操作。
比如params拦截器将请求参数解析出来,设置Action的属性。servletConfig拦截器负责将request和response对象传给Action的等等
拦截器可以动态的拦截发送到指定Action的请求,通过拦截器机制,我们可以在Action执行的前后插入某些代码。
通过这种方式,就可以把多个Action中重复的代码提取出来,放在拦截器中,从而提高更好的代码复用性。
理解DRY规则 DRY: Don‘t Repeat Yourself 意思是不要书写重复的代码。
对于软件开发的新手来说,开发软件时可能很多地方需要重复性的功能和代码,新手会直接选择复制粘贴即可。
一旦需要更改维护这段代码,就要修改很多地方,后期的维护简直是噩梦。 所以有经验的开发人员会将重复代码定义成一个方法,哪里需要哪里调用即可,更改的时候只用修改方法即可。
二、拦截器的意义
上面的例子中当有一天代码中需要调用另一个方法,或者是代码中的方法需要经常切换。
这时候我们又得打开源码,修改所有调用方法的地方。造成这个问题的关键在于 以硬编码的方式调用方法。
为了解决这个问题,我们需要一种机制,所有代码中无需硬编码调用某个方法,但实际上又可以调用方法的功能。
struts2的拦截器就是实现了这种需求。拦截器会在目标方法调用之前之后调用一些方法。
三、拦截器的实现原理
拦截器基于AOP(面向切面编程)思想。 AOP编程方式中,有三个重要的概念
-目标对象:被拦截方法的对象
-被插入的处理方法:定义在拦截器中,会在被拦截方法之前、之后自动调用的方法。方法不能独立存在,必须有载体,载体就是拦截器,拦截器就是包含处理方法的实例。
-代理对象:根据目标对象创建的代理对象。代理对象也称为AOP代理,系统动态生成一个对象,该对象将代替目标对象来使用。AOP代理包含了目标对象的所有方法,AOP代理中的方法会在特定位置插入拦截器方法,然后回调目标对象的处理方法,从而实现了执行目标方法之前或者之后调用拦截器方法。
四、struts2中的拦截器
Struts2框架拦截器
Struts 2框架提供了一个良好的开箱即用的拦截器列表,这些拦截器预先配置好并可以使用。 下面列出了几个重要的拦截器:
序号 | 拦截器和说明 |
---|---|
1 |
alias
允许参数在请求之间使用不同的别名。 |
2 |
checkbox
通过为未检查的复选框添加参数值false,以辅助管理复选框。 |
3 |
conversionError
将字符串转换为参数类型的错误信息放置到action的错误字段中。 |
4 |
createSession
自动创建HTTP会话(如果尚不存在)。 |
5 |
debugging
为开发人员提供一些不同的调试屏幕。 |
6 |
execAndWait
当action在后台执行时,将用户发送到中间的等待页面。 |
7 |
exception
映射从action到结果抛出的异常,允许通过重定向自动处理异常。 |
8 |
fileUpload
便于文件上传。 |
9 |
i18n 在用户会话期间跟踪选定的区域。 |
10 |
logger
通过输出正在执行的action的名称提供简单的日志记录。 |
11 |
params
设置action上的请求参数。 |
12 |
prepare
这通常用于执行预处理工作,例如设置数据库连接。 |
13 |
profile
允许记录action的简单分析信息。 |
14 |
scope
在会话或应用程序范围内存储和检索action的状态。 |
15 |
ServletConfig
提供可访问各种基于servlet信息的action。 |
16 |
timer
以action执行时间的形式提供简单的分析信息。 |
17 |
token
检查action的有效性,以防止重复提交表单。 |
18 |
validation
提供action的验证支持。 |
你可以阅读Struts 2文档,了解上述拦截器的完整信息。接下来我们会告诉你如何在Struts应用程序中使用拦截器。
五、拦截器使用
(一)配置拦截器
在struts.xml中定义拦截器 <interceptor name="拦截器名" class="拦截器类" /> 如果配置拦截器时需要传入拦截器参数,则需要使用param元素。 <interceptor name="拦截器名" class="拦截器类" >
<param name="参数名">参数值</param>
....
</interceptor> 还可以把多个拦截器组成拦截器栈
<interceptor-stack name="拦截器栈名">
<interceptor-ref name="拦截器一"/>
<interceptor-ref name="拦截器二"/>
....
</interceptor-stack>
(二)使用拦截器或拦截器栈
通过<intercept-ref name="拦截器名字" />使用拦截器
(三)自定义拦截器
实现自定义拦截器需要实现Interceptor接口
该接口有三个方法
-void init():拦截器实例化之后调用,只会执行一次,用于初始化资源 -void destory():拦截器销毁之前调用 -String intercept(ActionInvocation invoction)throws Exception:该方法是用户需要实现的拦截动作。 该方法的ActionInvocation包含了被拦截的action引用等所有数据,可以调用该参数的invoke方法放行,如果有下一个拦截器转到下一个拦截器,如果没有就转给Action类的方法。 struts2提供了AbstractInterceptor类实现了Interceptor接口,我们只需要继承这个类即可。
(四)举例:
第一步:实现拦截器:
public class MyInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("拦截器执行:动作方法之前");
//放行
String result=invocation.invoke();
System.out.println("拦截器执行:动作方法之后");
return result;
}
}
第二步:创建action类
public class Demo extends ActionSupport {
public String execute(){
System.out.println("执行动作类的execute方法");
return SUCCESS;
}
}
第三步:配置struts.xml文件
<package name="demo1" extends="struts-default">
//定义拦截器
<interceptors>
<interceptor name="myinterceptor" class="com.cad.struts2.interceptor.MyInterceptor"></interceptor>
</interceptors> <action name="demo1" class="com.cad.struts2.action.Demo">
//action中使用拦截器,如果action使用了拦截器,则默认的拦截器栈就失效
<interceptor-ref name="myinterceptor"></interceptor-ref>
<result>/Demo.jsp</result>
</action>
</package>
第四步:返回成功的jsp页面
<body>
demo1.jsp
<%System.out.println("demo1.jsp执行了"); %>
</body>
输出结果:
拦截器之前
执行动作类的execute方法
demo1.jsp执行了
拦截器之后
六、拦截器堆栈
你可以想象,为每个action配置的多个拦截器将很快变得极其难以管理。为此,拦截器使用拦截器堆栈进行管理。这里是直接从struts-default.xml文件展示的一个例子:
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
上面的堆栈称为basicStack,可以如下所述在你的配置中使用,此配置节点放置在<package ... />节点下。<interceptor-ref ... />标签引用的是在当前拦截器堆栈之前配置的拦截器或拦截器堆栈。因此非常重要的是在配置初始拦截器和拦截器堆栈时,确保name在所有拦截器和拦截器堆栈配置中是唯一的。
我们已经学习了如何将拦截器应用到action中,而拦截器堆栈的应用也是类似的。事实上,使用的标签也是一样的:
<action name="hello" class="com.tutorialspoint.struts2.MyAction">
<interceptor-ref name="basicStack"/>
<result>view.jsp</result>
</action
上述的“basicStack”注册将完整注册hello action的所使用的六个拦截器。要注意的是,拦截器按照它们被配置的顺序执行。例如,在上面的例子中,exception将首先执行,第二个将是servlet-config等。
七、拦截指定方法的拦截器
默认情况下,我们为某个action定义了拦截器,则这个拦截器会拦截该Action的所有方法,如果我们只需要拦截指定方法,此时需要使用struts2拦截器的方法过滤特性。 struts2提供了一个和MethodFilterInterceptor类,该类是AbstractInterceptor的子类。 该类重写了intercept方法,提供了一个doIntercept(ActionInvocation invocation)抽象方法。 该类重写的intercept方法已经实现了对Action的拦截行为,通过回调doIntercept来完成具体的拦截逻辑。 我们需要重写doIntercept方法来实现拦截逻辑。 实现方法过滤的拦截器和普通的拦截器并没有太大区别,但是这个类中增加了两个方法。
-public void setExcludeMethods(String excludeMethods):指定的方法都不会被拦截
-public void setIncludeMethods(String includeMethods):指定的方法会被拦截 如果一个方法同时被这两个方法指定,则这个方法会被拦截。
第一步:我们编写一个自定义拦截器:
public class DemoIntercept extends MethodFilterInterceptor { protected String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("拦截器执行:动作方法之前");
String result=invocation.invoke();
System.out.println("拦截器执行:动作方法之后");
return result;
} }
第二步:在struts.xml中配置:
<action name="demo1" class="com.cad.struts2.action.Demo">
<interceptor-ref name="myinterceptor">
//设置不会被拦截的方法
<param name="excludeMethods">execute</param>
//设置被拦截的方法
<param name="includeMethods">login,regist</param>
</interceptor-ref>
<result>/Demo.jsp</result>
</action>
八、拦截器小常识
(1)拦截器和struts2插件的关系
我们需要为struts2扩展新功能时,这时需要开发自己的拦截器,通常我们不可能去修改struts-default.xml文件
而通用功能的拦截器也不应该在某个指定的action中配置。这就需要在struts2插件的struts-plugin.xml文件中配置拦截器。
(2)配置默认拦截器
<default-interceptor-ref name="拦截器或者拦截器栈名"></default-interceptor-ref>
对于多个action都要使用的拦截器,避免了在多个action中重复指定拦截器。
(3)使用拦截器时配置参数
<interceptor-ref name="myinterceptor">
<param name="参数名"></param>
</interceptor-ref> 用拦截器时配置的参数如果和定义拦截器时配置的参数相同,那么会覆盖定义时的参数。
(4)覆盖拦截器栈里特定拦截器的参数
有时候,action需要使用拦截器栈,当使用这个拦截器栈,又需要覆盖指定拦截器的参数。
可以通过param来指定,name为 拦截器名字.参数名 <interceptor-ref name="mystack">
<param name="myintercept.name">参数值</param>
</interceptor-ref>
(5)拦截器执行顺序
invoke方法之前的动作谁排在前面谁执行。
invoke方法之后的动作谁排在后面先执行。
其实这是递归实现。 第一个拦截器执行完,调用invoke方法,如果有下一个拦截器,执行第二个拦截器,然后没有拦截器的话,就执行Action类中的方法,然后返回到第二个拦截器,第二个拦截器执行完毕,然后返回到第一个拦截器。
例子:实现登录权限控制
当我们访问main.jsp时,触发拦截器,如果没有登录,返回到登陆页面
第一步:先创建一个自定义拦截器:
public class LoginInterceptor extends MethodFilterInterceptor { protected String doIntercept(ActionInvocation invocation) throws Exception {
//获取session,判断session中是否有用户
HttpSession session=ServletActionContext.getRequest().getSession(); //没用户返回到input页面
Object obj=session.getAttribute("user");
if(obj==null){
return "input";
}
//有的话放行
String result=invocation.invoke();
return result;
} }
第二步:创建一个action类
public class DemoAction extends ActionSupport { //登陆方法,向session中设置
public String login(){
HttpSession session=ServletActionContext.getRequest().getSession();
session.setAttribute("user", "user");
return SUCCESS;
}
public String execute() throws Exception { return SUCCESS;
}
}
第三步:配置我们的struts.xml文件
<package name="demo8" extends="struts-default">
<!--定义拦截器-->
<interceptors>
<interceptor name="logininterceptor" class="com.cad.struts2.interceptor.LoginInterceptor"></interceptor>
</interceptors> <action name="login" class="com.cad.struts2.action.DemoAction" method="login">
<!--登陆成功,直接跳转到main页面-->
<result type="redirectAction">main</result>
</action> <!--执行main动作时,需要拦截器拦截-->
<action name="main" class="com.cad.struts2.action.DemoAction">
<result>/main.jsp</result>
<result name="input">/login.jsp</result>
<!--使用默认拦截器和我们的拦截器-->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="logininterceptor"></interceptor-ref>
</action>
</package>