(六)Struts2的拦截器

时间:2023-01-16 20:07:57

一、简介

拦截器体系是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>