Struts 2拦截器

Struts 2拦截器

学习内容 拦截器工作原理

Struts 2自带拦截器

自定义拦截器

能力目标

熟悉Struts 2拦截器工作原理

熟练使用和配置拦截器

本章简介

上一章我们深入学习了Struts 2框架的配置,包括Action的配置、Result的配置等等,使我们对Struts 2框架有了更深的了解。Struts 2的核心包括Action、Result和拦截器。拦截器是Struts 2的一个重要特性,实际上Struts 2框架的核心功能都是由拦截器负责完成的。

拦截器(Interceptor)能动态拦截Action调用的对象。它提供了一种机制使开发者可以在一个Action执行之前或执行之后插入需要的代码,同时它也是提供了一种可以提取Action中可重用代码的方式。本章将重点学习Struts 2拦截器。

​核心技能部分​

5.1 Struts 2的拦截器在第五章学习Struts 2体系结构时已经提到了拦截器,它主要出现在Action执行之前或执行之后,大家可以先回顾一下。对于Web应用程序来说,有很多的业务都是通用的,例如对请求数据的封装、数据的校验、防止表单重复提交等。早期的MVC框架将这些业务功能都写死在核心控制器中了,这直接导致框架的灵活性和可扩展性严重下降。

Struts 2将它的核心功能放到拦截器中实现而不是集中在核心控制器里,并且每个拦截器完成一个业务功能,不同的拦截器还能自由组合,这使得开发人员能够更加高效、灵活的使用Struts 2框架进行开发。

拦截器是动态拦截Action调用的对象,拦截器的方法可以在Action执行之前或之后自动执行,从而将通用的操作动态地插入到Action执行的前后,这跟我们平时组装电脑很类似,业务功能变成了可插拔式,需要哪个功能就“插入”一个拦截器,不需要就“拔出”,非常有利于系统的解耦。

单一的拦截器还可以灵活的组合在一起构成拦截器栈。拦截器是一个类,通过在struts.xml中进行配置来发挥作用。

5.1.1 拦截器工作原理拦截器围绕着Action和Result的执行而执行,如图5-1-1所示。Action被一系列的拦截器包裹,首先执行Action之前的拦截器,并按照顺序依次执行拦截器1、拦截器2、拦截器3……,在Action和Result执行过之后,拦截器会再一次执行,并按照相反的顺序依次执行,图5-1-2清晰的显示了这种顺序。在这一系列执行过程中,任何一个拦截器都可以直接返回,从而终止余下的拦截器的执行。

Struts 2拦截器_conversionError图5.1.1 拦截器工作原理

Struts 2拦截器_自定义拦截器_02图5.1.2 Struts 2拦截器时序图

5.1.2 Struts 2自带拦截器Struts 2框架提供了一系列功能强大的拦截器,它们实现了框架的大部分功能,同时在实际开发中我们也可以灵活应用这些Struts 2自带的拦截器。打开Struts 2源文件中的struts-default.xml文件,如图5.1.3所示,Struts 2自带的拦截器就配置在这个文件里。

Struts 2拦截器_servletConfig_03图5.1.3 Struts 2自带拦截器

单个拦截器由元素配置,name属性设置拦截器的逻辑名,class属性设置拦截器的实现类。

下面对Struts 2自带的拦截器进行简单介绍:

Ø checkbox:添加了对表单中checkbox自动处理的代码,将没有选中的checkbox的内容

设定为false,而HTML默认情况下不提交没有选中的checkbox。

conversionError:将错误信息从ActionContext中添加到Action的属性字段中。

createSession:自动创建HttpSession,用来为需要使用到HttpSession的拦截器服务。

execAndWait:在后台执行Action,同时将用户带到一个中间的等待页面。

exception:捕获异常并能根据异常类型映射到用户自定义的错误页面。

fileUpload:提供文件上传功能。

params:将请求中的参数设置到Action的属性上。

scope:将Action状态存入session和application的简单方法。

servletConfig:提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方

式访问。

token:防止表单重复提交。

tokenSession:和token一样,不过双击的时候把请求的数据存储在session中。

validation:执行数据校验。

workflow:调用Action的validate方法,一旦有错误返回就终止执行流程。

上面列出了Struts 2常用的拦截器,在实际应用中,我们经常需要组合不同的拦截器形成一个拦截器栈,Struts 2框架在struts-default.xml中定义了一个默认的拦截器栈defaultStack,如图5.1.4所示,这个拦截器栈组合了多个拦截器,并且这些拦截器的顺序都是经过精心设计的,能满足大多数Web应用的需求。在struts.xml中,我们自定义的包(package)通常都要继承struts-default包,而此包指定了defaultStack拦截器栈为默认拦截器栈,所以如果用户的自定义包继承了struts-default包,也会自动将defaultStack做为默认拦截器。如果用户没有为Action指定拦截器,系统将会自动把defaultStack拦截器栈作用于此Action。

Struts 2拦截器_自定义拦截器_04图5.1.4 defaultStack拦截器栈

5.2 配置拦截器拦截器的使用分为三个步骤:

(1) 实现拦截器类

(2) 在struts.xml中定义拦截器或拦截器栈

(3) 为Action配置拦截器或拦截器栈

如果我们使用的是Struts 2自带的拦截器,那么只需要进行第三步即可;如果我们使用的是自己开发的拦截器,就需要进行以上三步。下面是配置语法。

代码语言:javascript复制

... ...

... ...

/success.jsp

/login.jsp

所有的拦截器或拦截器栈都定义在元素内,单个拦截器由它的子元素s定义,name属性定义拦截器的逻辑名,class属性用来指定拦截器的实现类。

拦截器栈由元素定义,并需要通过子元素来引用已经定义好的拦截器作为栈元素,的name属性用来设置想要引用的拦截器的逻辑名。

定义好拦截器或拦截器栈以后,需要在Action的配置中为元素配置拦截器或拦截器栈,此时使用的也是,name同样设置为想要引用的拦截器的逻辑名,这里可以直接使用Struts的自带的拦截器。

5.3 自定义拦截器5.3.1 如何使用自定义的拦截器虽然Struts 2提过了如此丰富的拦截器,但是在实际应用中,我们仍需要自己开发拦截器以满足经常变化的业务需求。

自定义的拦截器类通常需要实现com.opensymphony.xwork2.interceptor.lnterceptor 接口,该接口的源代码如下所示。

代码语言:javascript复制public interface Interceptor {

void destroy();

void init();

String intercept (ActionInvocation invocation) throws Exception;

}

}该接口定义了三个方法:

init(),该方法执行于拦截器执行拦截之前,且每个拦截器只执行一次。因此,该方法的

主要用于打开某些资源。

destroy(),该方法与init()方法对应。在拦截器实例被销毁之前,系统将回调此方法,销

毁在init()方法中打开的资源。

intercept(Actionlnvocationinvocation),该方法是用户需要实现的拦截器动作,返回一个result配置字符串作为逻辑视图。该方法的ActionInvocation参数包含被拦截的Action的引用,可以通过调用该参数的invoke方法,将控制权转给下一个拦截器或者Action。详见表7-1-1所示。

表7-1-1 Actionlnvocation类的常用方法

​方法​

​描述​

Object getAction()

获得与拦截器关联的Action,注意进行强制类型转换。

String invoke()throws Exception

继续执行后面的拦截器或Action,可能抛出任何异常。

除此之外,Struts2还提供了一个com.opensymphony.xwork2.interceptor.Abstractlnterceptor类,该类提供了一个init方法和destroy方法的空实现。如果自定义的拦截器不需要申请资源,就无须实现这两个方法。所以,继承自Abstractlnterccptor类来实现自定义拦截器会更加简便。

​示例5.1​

考虑一般的WEB应用,基本都可分为前台和后台两部分,后台主要是对整个WEB应用进行管理,所以要使用后台必须先登录,后台部分可能包含了很多的Action和JSP视图,我们必须保证用户不能直接访问后台的每一个Action和JSP视图,或者说要想访问就必须先登录,这属于访问控制,这种情况非常适合使用拦截器来解决,接下来我们使用拦截器重构登录案例,把登录验证用拦截器来实现。

(1) 实现拦截器类

代码语言:javascript复制public class LoginInterceptor implements Interceptor {

public void destroy() {

}

public void init() {

}

public String intercept(ActionInvocation ai) throws Exception {

HttpSession session = ServletActionContext.getRequest().getSession();

User user = (User)session.getAttribute("loginUser");

if(user!=null)

return ai.invoke(); //登录成功则继续执行后面的拦截器或Action

else

return "error";

}

}

(2) 实现Action。

public class LoginAction extends ActionSupport{

private String name;

private String pwd;

//省略getter和setter方法

public String execute()

{

return SUCCESS;

}

}由于我们把登录业务放在了拦截器中实现,所以Action中就无需编写业务实现代码了,直接在execute方法中返回了SUCCESS。

(3) 登录视图页面login.jsp

代码语言:javascript复制

管理员登录
登录名称:
登录密码:

(4) 在struts.xml配置文件中配置LoginAction的拦截器。

代码语言:javascript复制

/index.jsp

/login.jsp

在上述代码中,我们首先使用在配置文件中定义了自己开发的拦截器,然后把该拦截器和Struts 2默认的defaultStack组成了一个拦截器栈,最后为名字是“login”的action指定了这个拦截器栈。

如果其他的Action也需要进行登录访问控制,就只需要为该Action配置实现登录验证的拦截器即可。

5.3.2 默认拦截器通常情况下,WEB应用的后台部分可能包含了很多的Action和JSP视图,为了实现访问控制,我们要给后台的每个Action都配置拦截器,虽然能够实现,但是过于繁琐,更好的解决方案就是给这些后台Action配置一个默认的拦截器(栈)。

默认拦截器(栈)定义在包(package)中,该包(package)中的所有Action都共享使用该默认拦截器(栈),每个包(package)中只能定义一个默认拦截器(栈)。

在struts.xml文件中使用元素来配置默认拦截器(栈),它是包(package)的子元素,该元素的name属性值是一个已经定义好的某个拦截器(栈)的名字。参考代码如下所示。

代码语言:javascript复制