一、Struts2的系统结构图
1. 整个结构就如同一个堆栈,除了Action以外,堆栈中的其他元素是Interceptor
2. Action位于堆栈的底部。由于堆栈"先进后出"的特性,如果我们试图把Action拿出来执行,我们必须首先把位于Action上端的Interceptor拿出来执行。这样,整个执行就形成了一个递归调用。
3. 每个位于堆栈中的Interceptor,除了需要完成它自身的逻辑,还需要完成一个特殊的执行职责。这个执行职责有3种选择:
1) 中止整个执行,直接返回一个字符串作为resultCode
2) 通过递归调用负责调用堆栈中下一个Interceptor的执行
3) 如果在堆栈内已经不存在任何的Interceptor,调用Action
Struts2的拦截器结构的设计,实际上是一个典型的责任链模式的应用。首先将整个执行划分成若干相同类型的元素,每个元素具备不同的逻辑责任,并将他们纳入到一个链式的数据结构中(我们可以把堆栈结构也看作是一个递归的链式结构),而每个元素又有责任负责链式结构中下一个元素的执行调用。
这样的设计,从代码重构的角度来看,实际上是将一个复杂的系统,分而治之,从而使得每个部分的逻辑能够高度重用并具备高度可扩展性。所以,Interceptor结构实在是Struts2/Xwork设计中的精华之笔。
二、拦截器基本概念
1.Struts中拦截器就是一个类,实现了Interceptor 接口的一个类。
2.Struts中拦截器和Servlet中的Filter有类似的功能,从字面意思来看,Struts 拦截器就是在目标对应执行之前或之后做一些事情,其实Struts中的拦截器的实现也是一样,在具体Action的被调用之前或之后可以做一些操作,采用配置化的方法进行管理,使用起来比较简单。
3.拦截器采用的动态代理模式实现的在目标执行之前或之后插入必要的辅助业务。其实采用的是一种AOP的思想,来降低系统耦合。
三、配置拦截器的方法
方法一:配置拦截器法
1、现在struts2.xml中
<interceptors> <interceptor name="login" class="com.bochy.struts2.intecepter.LenIntecepter"></interceptor> </interceptors>
2、在相应的action中,添加相应的拦截器。注意,如果还要系统提供的功能,要配置defaultStack,并且要放在自定义拦截器的前面。
<action name="login" class="com.bochy.struts2.action.login"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="login"></interceptor-ref> <result name="success">index2.jsp</result> <result name="input">index.jsp</result> </action>
方法二:配置拦截器栈
如果自定义的拦截器比较多,就可以配置一个拦截器栈,把所有的拦截器都放在这个栈里。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" > <struts> <package name="a" extends="struts-default"> <interceptors> <interceptor-stack name="loginstack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="login"></interceptor-ref> </interceptor-stack> <interceptor name="login" class="com.bochy.struts2.intecepter.LenIntecepter"></interceptor> </interceptors> <action name="login" class="com.bochy.struts2.action.login"> <interceptor-ref name="loginstack"></interceptor-ref> <result name="success">index2.jsp</result> <result name="input">index.jsp</result> </action> </package> </struts>
方法三:配置默认拦截器
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" > <struts> <package name="a" extends="struts-default"> <interceptors> <interceptor-stack name="loginstack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="login"></interceptor-ref> </interceptor-stack> <interceptor name="login" class="com.bochy.struts2.intecepter.LenIntecepter"></interceptor> </interceptors> <default-interceptor-ref name="loginstack"></default-interceptor-ref> <action name="login" class="com.bochy.struts2.action.login"> <result name="success">index2.jsp</result> <result name="input">index.jsp</result> </action> </package> </struts>
四、实现拦截器的方法
需求:编写一个Struts2的拦截器,对输入用户名和密码的长度进行检验,只要其中一个小于6,就要求客户重新输入,导向登录页面。
方法一:实现Interceptor接口
package com.bochy.struts2.intecepter; import com.bochy.struts2.action.login; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class LenIntecepter implements Interceptor{ private static final long serialVersionUID = 1L; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void init() { // TODO Auto-generated method stub } @Override public String intercept(ActionInvocation invocation) throws Exception { Object ob= invocation.getAction(); if(ob instanceof login) { login l=(login)ob; String name=l.getName(); String pwd=l.getPassword(); if(name.length()<6||pwd.length()<6){ System.out.println("拦截器实行了拦截,转向登录页面!"); return "input"; } } return invocation.invoke();//执行下一个拦截器或者Action } }
方法二:继承AbstractIntercepter
package com.bochy.struts2.intecepter; import com.bochy.struts2.action.login; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class LenIntecepter extends AbstractInterceptor{ private static final long serialVersionUID = 1L; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void init() { // TODO Auto-generated method stub } @Override public String intercept(ActionInvocation invocation) throws Exception { Object ob= invocation.getAction(); if(ob instanceof login) { login l=(login)ob; String name=l.getName(); String pwd=l.getPassword(); if(name.length()<6||pwd.length()<6){ System.out.println("拦截器实行了拦截,转向登录页面!"); return "input"; } } return invocation.invoke(); } }
五、 配置拦截器的参数
需要注意的是,不要再init()方法中给参数初始化,否则参数的值为init()中的值,不是配置文件中的值。
在一个拦截器中定义两个变量type和number,分别给他们setter和getter方法。
package com.bochy.struts2.intecepter; import com.bochy.struts2.action.login; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class LenIntecepter extends AbstractInterceptor{ private static final long serialVersionUID = 1L; @Override public void destroy() { System.out.println("调用拦截器的destroy()"); } @Override public void init() { System.out.println("调用拦截器的init(),完成一些初始化工作:"); } @Override public String intercept(ActionInvocation invocation) throws Exception { Object ob= invocation.getAction(); System.out.println("intercept number:"+number); System.out.println("intercept type:"+type); if(ob instanceof login) { login l=(login)ob; String name=l.getName(); String pwd=l.getPassword(); if(name.length()<6||pwd.length()<6){ System.out.println("拦截器实行了拦截,转向登录页面!"); return "input"; } } return invocation.invoke(); } private int number; public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String getType() { return type; } public void setType(String type) { this.type = type; } private String type; }
在定义拦截器中,在<interceptor>标签中给它赋值
<interceptors> <interceptor-stack name="loginstack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="login"> </interceptor-ref> </interceptor-stack> <interceptor name="login" class="com.bochy.struts2.intecepter.LenIntecepter"> <param name="type">checklength</param> <param name="number">30</param> </interceptor> </interceptors> <default-interceptor-ref name="loginstack"></default-interceptor-ref> 或者在引用时在<interceptor-ref>中给它赋值(动态), <interceptors> <interceptor-stack name="loginstack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="login"> <param name="type">checklength</param> <param name="number">30</param> </interceptor-ref> </interceptor-stack> <interceptor name="login" class="com.bochy.struts2.intecepter.LenIntecepter"> </interceptor> </interceptors> <default-interceptor-ref name="loginstack"></default-interceptor-ref>
如果定义和引用都给某个变量赋值,引用有效,覆盖定义的。
六、系统拦截器
Struts2中自带了的拦截器、拦截链(在struts-default.xml中),大家还是应该了解下的,具体如下:
拦截器 | 名字 | 说明 |
Alias Interceptor | alias | 在不同请求之间将请求参数在不同名字件转换,请求内容不变 |
Chaining Interceptor | chain | 让前一个Action的属性可以被后一个Action访问,现在和chain类型的result(<result type=”chain”>)结合使用。 |
Checkbox Interceptor | checkbox | 添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox。 |
Cookies Interceptor | cookies | 使用配置的name,value来是指cookies |
Conversion Error Interceptor | conversionError | 将错误从ActionContext中添加到Action的属性字段中。 |
Create Session Interceptor | createSession | 自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。 |
Debugging Interceptor | debugging | 提供不同的调试用的页面来展现内部的数据状况。 |
Execute and Wait Interceptor | execAndWait | 在后台执行Action,同时将用户带到一个中间的等待页面。 |
Exception Interceptor | exception | 将异常定位到一个画面 |
File Upload Interceptor | fileUpload | 提供文件上传功能 |
I18n Interceptor | i18n | 记录用户选择的locale |
Logger Interceptor | logger | 输出Action的名字 |
Message Store Interceptor | store | 存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。 |
Model Driven Interceptor | model-driven | 如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。 |
Scoped Model Driven | scoped-model-driven | 如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用Action的setModel方法将其放入Action内部。 |
Parameters Interceptor | params | 将请求中的参数设置到Action中去。 |
Prepare Interceptor | prepare | 如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。 |
Scope Interceptor | scope | 将Action状态存入session和application的简单方法。 |
Servlet Config Interceptor | servletConfig | 提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。 |
Static Parameters Interceptor | staticParams | 从struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中。 |
Roles Interceptor | roles | 确定用户是否具有JAAS指定的Role,否则不予执行。 |
Timer Interceptor | timer | 输出Action执行的时间 |
Token Interceptor | token | 通过Token来避免双击 |
Token Session Interceptor | tokenSession | 和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中 |
Validation Interceptor | validation | 使用action-validation.xml文件中定义的内容校验提交的数据。 |
Workflow Interceptor | workflow | 调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面 |
Parameter Filter Interceptor | N/A | 从参数列表中删除不必要的参数 |
Profiling Interceptor | profiling | 通过参数激活profile |
如果开发者定义的package继承struts2框架的默认包struts-default,则可以自由使用上面定义的拦截器。
七、系统拦截器的使用
使用系统拦截器,就只要在action中调用即可,timer拦截器可以实现输出Action的执行时间,因此timer拦截器被称为耗时拦截器。
<interceptors> <interceptor-stack name="loginstack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="login"></interceptor-ref> <interceptor-ref name="timer"></interceptor-ref> </interceptor-stack> <interceptor name="login" class="com.bochy.struts2.intecepter.LenIntecepter"> </interceptor> </interceptors> <default-interceptor-ref name="loginstack"></default-interceptor-ref>
八、上机练习
1、 自定义一个Struts拦截器,对评论内容过滤,出现“AB“的文字用*代替。
2、 自定义一个拦截器,实现用户权限管理功能,即只有登录以后,才可以访问主页。