struts2详解
Struts2的工作原理

上图来源于Struts2官方站点,是Struts 2 的整体结构。
一个请求在Struts2框架中的处理大概分为以下几个步骤(可查看源码:https://github.com/apache/struts):
1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher(现已过时)被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
在上述过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。
Struts开源架构很好的实现了MVC模式,MVC即Model-View-Controller的缩写,是一种常用的设计模式。MVC 减弱了业务逻辑接口和数据接口之间的耦合,以及让视图层更富于变化。MVC的工作原理,如下图1所示:

Struts 是MVC的一种实现,它将 Servlet和 JSP 标记(属于 J2EE 规范)用作实现的一部分。Struts继承了MVC的各项特性,并根据J2EE的特点,做了相应的变化与扩展。下面是Struts实现MVC的原理。如下图2所示:

控制:通过图2大家可以看到有一个XML文件Struts-config.xml,与之相关联的是Controller, ,它可以称作为Struts神经中枢。
视图:主要由JSP生成页面完成视图,Struts提供丰富的JSP 标签库: Html,Bean,Logic,Template等,这有利于分开在Struts中,承担MVC中Controller角色的是一个Servlet,叫ActionServlet。ActionServlet是一个通用的控制组件。这个控制组件提供了处理所有发送到Struts的HTTP请求的入口点。它截取和分发这些请求到相应的动作类(这些动作类都是Action类的子类)。另外控制组件也负责用相应的请求参数填充 Action From(通常称之为FromBean),并传给动作类(通常称之为ActionBean)。动作类实现核心商业逻辑,它可以访问java bean 或调用EJB。最后动作类把控制权传给后续的JSP 文件,后者生成视图。所有这些控制逻辑利用Struts-config.xml文件来配置。表现逻辑和程序逻辑。
模型:模型以一个或多个java bean的形式存在。这些bean分为三类:Action Form、Action、JavaBean or EJB。Action Form通常称之为FormBean,封装了来自于Client的用户请求信息,如表单信息。Action通常称之为ActionBean,获取从ActionSevlet传来的FormBean,取出FormBean中的相关信息,并做出相关的处理,一般是调用Java Bean或EJB等。
流程:在Struts中,用户的请求一般以*.do作为请求服务名,所有的*.do请求均被指向ActionSevlet,ActionSevlet根据Struts-config.xml中的配置信息,将用户请求封装成一个指定名称的FormBean,并将此FormBean传至指定名称的ActionBean,由ActionBean完成相应的业务操作,如文件操作,数据库操作等。每一个*.do均有对应的FormBean名称和ActionBean名称,这些在Struts-config.xml中配置。
struts2中struts.xml配置文件详解
struts.xml的常用配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 所有匹配*.action的请求都由struts2处理 -->
<constant name="struts.action.extension" value="action" />
<!-- 是否启用开发模式 -->
<constant name="struts.devMode" value="true" />
<!-- struts配置文件改动后,是否重新加载 -->
<constant name="struts.configuration.xml.reload" value="true" />
<!-- 设置浏览器是否缓存静态内容 -->
<constant name="struts.serve.static.browserCache" value="false" />
<!-- 请求参数的编码方式 -->
<constant name="struts.i18n.encoding" value="utf-8" />
<!-- 每次HTTP请求系统都重新加载资源文件,有助于开发 -->
<constant name="struts.i18n.reload" value="true" />
<!-- 文件上传最大值 -->
<constant name="struts.multipart.maxSize" value="104857600" />
<!-- 让struts2支持动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<!-- Action名称中是否还是用斜线 -->
<constant name="struts.enable.SlashesInActionNames" value="false" />
<!-- 允许标签中使用表达式语法 -->
<constant name="struts.tag.altSyntax" value="true" />
<!-- 对于WebLogic,Orion,OC4J此属性应该设置成true -->
<constant name="struts.dispatcher.parametersWorkaround" value="false" />
<package name="basePackage" extends="struts-default">
</package>
</struts>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd" >
<struts>
<!-- include节点是struts2中组件化的方式 可以将每个功能模块独立到一个xml配置文件中 然后用include节点引用 -->
<include file="struts-default.xml"></include>
<!-- package提供了将多个Action组织为一个模块的方式
package的名字必须是唯一的 package可以扩展 当一个package扩展自
另一个package时该package会在本身配置的基础上加入扩展的package
的配置 父package必须在子package前配置
name:package名称
extends:继承的父package名称
abstract:设置package的属性为抽象的 抽象的package不能定义action 值true:false
namespace:定义package命名空间 该命名空间影响到url的地址,例如此命名空间为/test那么访问是的地址为http://localhost:8080/struts2/test/XX.action
-->
<package name="com.kay.struts2" extends="struts-default" namespace="/test">
<interceptors>
<!-- 定义拦截器
name:拦截器名称
class:拦截器类路径
-->
<interceptor name="timer" class="com.kay.timer"></interceptor>
<interceptor name="logger" class="com.kay.logger"></interceptor>
<!-- 定义拦截器栈 -->
<interceptor-stack name="mystack">
<interceptor-ref name="timer"></interceptor-ref>
<interceptor-ref name="logger"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 定义默认的拦截器 每个Action都会自动引用
如果Action中引用了其它的拦截器 默认的拦截器将无效 -->
<default-interceptor-ref name="mystack"></default-interceptor-ref>
<!-- 全局results配置 -->
<global-results>
<result name="input">/error.jsp</result>
</global-results>
<!-- Action配置 一个Action可以被多次映射(只要action配置中的name不同)
name:action名称
class: 对应的类的路径
method: 调用Action中的方法名
-->
<action name="hello" class="com.kay.struts2.Action.LoginAction">
<!-- 引用拦截器
name:拦截器名称或拦截器栈名称
-->
<interceptor-ref name="timer"></interceptor-ref>
<!-- 节点配置
name : result名称 和Action中返回的值相同
type : result类型 不写则选用superpackage的type struts-default.xml中的默认为dispatcher
-->
<result name="success" type="dispatcher">/talk.jsp</result>
<!-- 参数设置
name:对应Action中的get/set方法
-->
<param name="url">http://www.sina.com</param>
</action>
</package>
</struts>
一个Action内包含多个请求处理方法的处理
Struts1提供了DispatchAction,从而允许一个Action内包含多个请求处理方法。Struts2也提供了类似的功能。
处理方式主要有以下三种方式:
1. 1 动态方法调用:
<!-- 动态方法调用HTML标签与Struts2标签 -->
<form action="computeAction!add.action" name="from" >
<s:form action="computeAction!add.action" name="form" theme="simple" >
则用户的请求将提交到名为”computeAction”的Action实例,Action实例将调用名为”add”方法来处理请求。
当指定调用某一方法来处理请求时,就不会走默认执行处理请求的execute()方法。
注意:要使用动态方法调用,必须设置Struts2允许动态方法调用,通过设置struts.enable.DynamicMethodInvocation常量来完成,该常量属性的默认值是true。
<struts>
<!--
//禁用动态方法调用,默认为true启用,false禁用
constant:name="struts.enable.DynamicMethodInvocation"
-->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
</struts>
示列:简单的一个加法和减法例子。
1. index.jsp用户在页面输入两个数字,选择相加,或者相减
当用户点击加或减需要走同一个Action但处理请求方法不同,这里使用了js动态选择。
<body>
<!-- 动态方法调用 使用:Struts2标签也可以使用HTML标签 -->
<s: name="form" theme="simple" >
num1:<s:textfield name="num1" />
num2:<s:textfield name="num2" />
<s:submit type="button" value="加" onclick="computeMethod('add')" />
<s:submit type="button" value="减" onclick="computeMethod('subtract')" />
</s:form>
<!-- js -->
<script type="text/javascript">
function computeMethod(op){
document.form.action="computeAction!"+op;//动态选择处理请求的方法
document.form.submit();//提交
}
</script>
</body>
2. struts.xml配置信息,启用动态方法调用(可选)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" >
<struts>
<!--
//禁用动态方法调用,默认为true启用,false禁用
constant:name="struts.enable.DynamicMethodInvocation"
-->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<package name="struts2" extends="struts-default">
<action name="computeAction" class="com.struts.ComputeAction" >
<result name="fruitPage" >/fruit.jsp</result>
</action>
</package>
</struts>
3. ComputeAction控制器的类处理请求
package com.struts;
/**
* Struts2控制器的类
* @author asus
*
*/
public class ComputeAction {
/** 属性 */
private int num1;
private int num2;
private int fruit;//结果
/** 若请求为指定操作方法默认执行execute()方法 */
public String execute(){
System.out.println("当调用其它方法就不会走这个方法!");
return "";
}
/** 执行处理加法 */
public String add(){
this.fruit=num1+num2;//加
return "fruitPage";
}
/** 执行处理减法 */
public String subtract(){
this.fruit=num1-num2;//减
return "fruitPage";
}
/** JavaBean */
public int getNum1() {
return num1;
}
public void setNum1(int num1) {
this.num1 = num1;
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
public int getFruit() {
return fruit;
}
public void setFruit(int fruit) {
this.fruit = fruit;
}
}
4. fruit.jsp响应结果的页面
<body>
<!-- 结果页面 -->
计算结果:<s:property value="fruit" />
</body>
1.2Action配置method属性(示列与以上代码大多一致,只修改有变更的):
将Action类中的每一个处理方法都定义成一个逻辑Action方法。
1. index.jsp页面
<body>
<!-- Action配置method属性 使用:Struts2标签也可以使用HTML标签 -->
<s:form name="form" theme="simple" >
num1:<s:textfield name="num1" />
num2:<s:textfield name="num2" />
<s:submit type="button" value="加" onclick="computeMethod('addAction')" />
<s:submit type="button" value="减" onclick="computeMethod('subtractAction')" />
</s:form>
<!-- js -->
<script type="text/javascript">
function computeMethod(op){
document.form.action=op;//动态选择处理请求的方法
document.form.submit();//提交
}
</script>
</body>
2. struts.xml配置信息
<?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="struts2" extends="struts-default">
<action name="addAction" class="com.struts.ComputeAction" method="add" >
<result name="fruitPage" >/fruit.jsp</result>
</action>
<action name="subtractAction" class="com.struts.ComputeAction" method="subtract" >
<result name="fruitPage" >/fruit.jsp</result>
</action>
</package>
</struts>
通过action元素的method属性来指定Action执行时调用的方法。
优点:使得以更加安全的方式来实现动态方法的调用,不让别人看到你的实现方法。
缺点:繁琐,一个处理请求的方法要跟一个action。
Struts2根据method属性查找方法有两种途径:
使用动态方法调用和method属性的区别:
1.通过以上三个struts.xml中的配置信息例子来说,他们的共同点是都在操作同一个Action。
2.<form action="">中请求地址不同。
3.动态方法的返回值相同,则会通过result进入一个页面。而method属性就算两个方法的返回值相同但进去不同的result,可能会进入两个不同的页面。
由上可以分析出:
(1)如果使用同一个Action,不同的处理请求的方法,响应使用相同的配置(result等)则使用动态方法调用。
(2)如果使用同一个Action,不同的处理请求的方法,响应分别使用不同的配置,则使用action元素的method属性,为同一个Action配置多个名称。
1.3使用通配符映射(wildcard mappings)方式(示列与以上代码大多一致,只修改有变更的):
1. index.jsp页面只改动了js部分。
<body>
<!-- 使用通配符映射(wildcard mappings)方式 使用:Struts2标签也可以使用HTML标签 -->
<s:form name="form" theme="simple" >
num1:<s:textfield name="num1" />
num2:<s:textfield name="num2" />
<s:submit type="button" value="加" onclick="computeMethod('addAction')" />
<s:submit type="button" value="减" onclick="computeMethod('subtractAction')" />
</s:form>
<!-- js -->
<script type="text/javascript">
function computeMethod(op){
document.form.action=op;//相比mothod属性改动只有这里
document.form.submit();//提交
}
</script>
</body>
2.struts.xml的配置信息
在使用method属性来实现同一个Action的不同方法处理不同的请求时,会发现,随着方法的增多,从而导致大量的Action配置,这时我们就需要通过使用通配符来解决Action配置过多的方法。
在配置<action.../>元素时,需要指定name、class、method属性。其中name属性可支持通配符,然后可以在class、method属性中使用表达式。通配符用星号 * 表示。
<?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="struts2" extends="struts-default">
<action name="*Action" class="com.struts.ComputeAction" method="{1}" >
<result name="fruitPage" >/fruit.jsp</result>
<!-- <result name="fruitPage" >/{1}.jsp</result>表达式也可以写在这里 -->
</action>
</package>
</struts>
实际上这一点从原理上来讲可以理解,default-action-ref这个配置的意思是当用户在点击了没有定义的action时,如果struts没有找到用户定义的action名称,则会自动跳转到该默认定义的action中。
个人觉得地址栏中项目后不写名称和名称不存在是两个概念。
示列:
1. struts.xml 就在通配符例子中配置上默认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="struts2" extends="struts-default">
<!-- 配置默认Action -->
<default-action-ref name="defaultAction"></default-action-ref>
<action name="defaultAction">
<result>/error.jsp</result>
</action>
<!-- 通配符映射(wildcard mappings) -->
<action name="*Action" class="com.struts.ComputeAction" method="{1}" >
<result name="fruitPage" >/fruit.jsp</result>
<!-- <result name="fruitPage" >/{1}.jsp</result>表达式也可以写在这里 -->
</action>
</package>
</struts>
2. index.jsp页面 这里我们把提交的url :Action地址链接,写错打断,当提交时找不到对应的Action,则会进入默认Action,进入error.jsp页面
<body>
<!-- 使用通配符映射(wildcard mappings)方式 使用:Struts2标签也可以使用HTML标签 -->
<s:form name="form" theme="simple" >
num1:<s:textfield name="num1" />
num2:<s:textfield name="num2" /> <!-- 测试默认Action,当提交的Action地址错误。则会走默认Action -->
<s:submit type="button" value="加" onclick="computeMethod('ssss')" /><!-- 把动态url地址乱写 -->
<s:submit type="button" value="减" onclick="computeMethod('subtractActios')" /><!-- 或在url地址中多加字符 -->
</s:form>
<!-- js -->
<script type="text/javascript">
function computeMethod(op){
document.form.action=op;//改动只有这里
document.form.submit();//提交
}
</script>
</body>
3. error.jsp 创建此页面查看效果
<body>
错误页面。!
未找到,Action实例时会默认走此页面!
</body>
3.处理结果
Struts2的Action处理完用户请求后,将返回一个普通字符串,整个普通字符串就是一个逻辑视图名。Struts2通过配置逻辑视图名和物理视图资源之间的映射关系,一旦系统收到Action返回的某个逻辑视图名,系统就会把对应的物理视图资源呈现给浏览者。
<!-- 全局结果可满足一个包中多个Action共享一个结果,也就是说,当多个Action中都有一个重复的result时就可以使用全局结果,也就是说公共的result -->
<global-results>
<result name="fruitPage" type="dispatcher" >/fruit.jsp</result>
</global-results>
| 名字 | 说明 |
| chain | 用来处理Action链 |
| dispatcher | 用来转向页面,通常处理JSP,这是默认的结果类型 |
| freeMarker | 处理FreeMarker模板 |
| httpHeader | 用来控制特殊的Http行为 |
| redirect | 重定向到一个URL |
| redirect-action | 重定向到一个Action |
| stream | 向浏览器发送InputSream对象,通常用来处理文件下载 |
| velocity | 处理Velocity模板 |
| xslt | 处理XML/XLST模板 |
| plaintext | 显示原始文件内容,例如文件源代码 |
| tiles | 结合Tile使用 |
另外第三方的Result类型还包括JasperReports Plugin,专门用来处理JasperReport类型的报表输出;Jfreechart Plugin;JSF Plugin。
常用示列:
1.struts.xml
<?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="struts2" extends="struts-default">
<!-- 默认Action -->
<default-action-ref name="defaultAction"></default-action-ref>
<!-- 全局结果可满足一个包中多个Action共享一个结果,也就是说,当多个Action中都有一个重复的result时就可以使用全局结果,也就是说公共的result
<global-results>
<result name="fruitPage" type="dispatcher" >/fruit.jsp</result>
</global-results> -->
<action name="defaultAction">
<result>/error.jsp</result>
</action>
<!-- 通配符映射(wildcard mappings) -->
<action name="*Action" class="com.struts.ComputeAction" method="{1}" >
<!--1
表达式{1}也可以写在url连接中,class,name中都可以写,也可以写多少,索引从1开始
<result name="fruitPage" >/{1}.jsp</result> -->
<!--2
默认dispatcher转发跳转
<result name="fruitPage" type="dispatcher" >/fruit.jsp</result> -->
<!--3
重定向跳转
<result name="fruitPage" type="redirect" >/fruit.jsp</result> -->
<!--4
redirectAction: Action实例 与另一个Action实例互相跳转
<result name="fruitPage" type="redirectAction" >skipAction</result> -->
<!--4.1
使用感叹号指定跳转方法,xml会显示报错,但可以用。 使用?&不能在Action实例中带参数
<result name="fruitPage" type="redirectAction" >skipAction!add</result> -->
<!--4.2
跳转Action带参数的方式:
actionName:跳转Action的名称
method:跳转Action实例中的哪个方法
num:带的参数,可写固定,可使用${属性名}取上一个Action实例中的属性,实现动态传值。
-->
<result name="fruitPage" type="redirectAction" >
<param name="actionName">skipAction</param>
<param name="method">add</param>
<param name="num1">${num1}</param>
<param name="num2">${num2}</param>
</result>
</action>
<action name="skipAction" class="com.struts.SkipAction" >
<result name="success" type="dispatcher" >/fruit.jsp</result>
</action>
</package>
</struts>
2.再创建一个SkipAction 控制器的类
package com.struts;
/**
* 测试与dispatcherAction之间的传值
* @author asus
*
*/
public class SkipAction {
/** 接收computeAction实例传过来属性 */
private int num1;
private int num2;
private int result;//结果返回给页面
public String execute(){
System.out.println("当指定要走的方法时不会走此方法");
return "success";
}
/**添加的方法 */
public String add(){
result=num1+num2;
System.out.println("走add方法!");
return "success";
}
/** Get,Set方法 */
public int getNum1() {
return num1;
}
public void setNum1(int num1) {
this.num1 = num1;
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
}
如何使用struts2拦截器,或者自定义拦截器。特别注意,在使用拦截器的时候,在Action里面必须最后一定要引用struts2自带的拦截器缺省堆栈defaultStack,如下(这里我是引用了struts2自带的checkbox拦截器):
<interceptor-ref name="checkbox">
<param name="uncheckedValue">0</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>(必须加,否则出错)
也可以改为对全局Action设置自己需要的拦截器,如下:
在struts.xml里面定义全局的配置设置
<package name="struts-shop" extends="struts-default">
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="checkbox">
<param name="uncheckedValue">0</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack"/>(这句是设置所有Action自动调用的拦截器堆栈)
</package>
struts-action.xml里面配置Action如下:
<package name="LogonAdmin" extends="struts-shop">(这里扩展struts.xml里面定义的配置就可以了)
<action name="logon" class="logonAction">
<result>/jsp/smeishop/admin/index.jsp</result>
<result name="error">/jsp/smeishop/admin/logon.jsp</result>
<result name="input">/jsp/smeishop/admin/logon.jsp</result>
</action>
<action name="logout" class="logoutAction">
<result>/jsp/smeishop/admin/logon.jsp</result>
</action>
</package>
你的拦截器可以正常工作了!!HOHO
以下是参考资料
struts2自带的配置及其拦截器配置
Struts2 拦截器 [Interceptor]

拦截器的工作原理如上图,每一个Action请求都包装在一系列的拦截器的内部。拦截器可以在Action执行直线做相似的操作也可以在Action执行直后做回收操作。
每一个Action既可以将操作转交给下面的拦截器,Action也可以直接退出操作返回客户既定的画面。
如何自定义一个拦截器?
自定义一个拦截器需要三步:
1 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。
2 在strutx.xml中注册上一步中定义的拦截器。
3 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。
Interceptor接口声明了三个方法:
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
Init方法在拦截器类被创建之后,在对Action镜像拦截之前调用,相当于一个post-constructor方法,使用这个方法可以给拦截器类做必要的初始话操作。
Destroy方法在拦截器被垃圾回收之前调用,用来回收init方法初始化的资源。
Intercept是拦截器的主要拦截方法,如果需要调用后续的Action或者拦截器,只需要在该方法中调用invocation.invoke()方法即可,在该方法调用的前后可以插入Action调用前后拦截器需要做的方法。如果不需要调用后续的方法,则返回一个String类型的对象即可,例如Action.SUCCESS。
另外AbstractInterceptor提供了一个简单的Interceptor的实现,这个实现为
public abstract class AbstractInterceptor implements Interceptor {
public void init() {
}
public void destroy() {
}
public abstract String intercept(ActionInvocation invocation) throws Exception;
}
在不需要编写init和destroy方法的时候,只需要从AbstractInterceptor继承而来,实现intercept方法即可。
我们尝试编写一个Session过滤用的拦截器,该拦截器查看用户Session中是否存在特定的属性(LOGIN属性)如果不存在,中止后续操作定位到LOGIN,否则执行原定操作,代码为:
public class CheckLoginInterceptor extends AbstractInterceptor {
public static final String LOGIN_KEY = "LOGIN";
public static final String LOGIN_PAGE = "global.login";
public String intercept(ActionInvocation actionInvocation) throws Exception {
System.out.println("begin check login interceptor!");
// 对LoginAction不做该项拦截
Object action = actionInvocation.getAction();
if (action instanceof LoginAction) {
System.out.println("exit check login, because this is login action.");
return actionInvocation.invoke();
}
// 确认Session中是否存在LOGIN
Map session = actionInvocation.getInvocationContext().getSession();
String login = (String) session.get(LOGIN_KEY);
if (login != null && login.length() > 0) {
// 存在的情况下进行后续操作。
System.out.println("already login!");
return actionInvocation.invoke();
} else {
// 否则终止后续操作,返回LOGIN
System.out.println("no login, forward login page!");
return LOGIN_PAGE;
}
}
}
注册拦截器
<interceptors>
<interceptor
name="login"
class="com.jpleasure.teamware.util.CheckLoginInterceptor"/>
<interceptor-stack name="teamwareStack">
<interceptor-ref name="login"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
将上述拦截器设定为默认拦截器:
<default-interceptor-ref name="teamwareStack"/>
这样在后续同一个package内部的所有Action执行之前都会被login拦截。
Struts2(XWork)提供的拦截器的功能说明:
|
拦截器 |
名字 |
说明 |
|
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 |
注册并引用Interceptor
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="timer" class=".."/>
<interceptor name="logger" class=".."/>
</interceptors>
<action name="login" class="tutorial.Login">
<interceptor-ref name="timer"/>
<interceptor-ref name="logger"/>
<result name="input">login.jsp</result>
<result name="success"
type="redirect-action">/secure/home</result>
</action>
</package>
可以将多个拦截器合并在一起作为一个堆栈调用,当一个拦截器堆栈被附加到一个Action的时候,要想Action执行,必须执行拦截器堆栈中的每一个拦截器。
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="timer" class=".."/>
<interceptor name="logger" class=".."/>
<interceptor-stack name="myStack">
<interceptor-ref name="timer"/>
<interceptor-ref name="logger"/>
</interceptor-stack>
</interceptors>
<action name="login" class="tutuorial.Login">
<interceptor-ref name="myStack"/>
<result name="input">login.jsp</result>
<result name="success"
type="redirect-action">/secure/home</result>
</action>
</package>
上述说明的拦截器在默认的Struts2应用中,根据惯例配置了若干个拦截器堆栈,详细情参看struts-default.xml
其中有一个拦截器堆栈比较特殊,他会应用在默认的每一个Action上。
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo"..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
每一个拦截器都可以配置参数,有两种方式配置参数,一是针对每一个拦截器定义参数,二是针对一个拦截器堆栈统一定义所有的参数,例如:
<interceptor-ref name="validation">
<param name="excludeMethods">myValidationExcudeMethod</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
或者
<interceptor-ref name="defaultStack">
<param name="validation.excludeMethods">myValidationExcludeMethod</param>
<param name="workflow.excludeMethods">myWorkflowExcludeMethod</param>
</interceptor-ref>
每一个拦截器都有两个默认的参数:
excludeMethods - 过滤掉不使用拦截器的方法和
includeMethods – 使用拦截器的方法。
需要说明的几点:
1 拦截器执行的顺序按照定义的顺序执行,例如:
<interceptor-stack name="xaStack">
<interceptor-ref name="thisWillRunFirstInterceptor"/>
<interceptor-ref name="thisWillRunNextInterceptor"/>
<interceptor-ref name="followedByThisInterceptor"/>
<interceptor-ref name="thisWillRunLastInterceptor"/>
</interceptor-stack>
的执行顺序为:
thisWillRunFirstInterceptor
thisWillRunNextInterceptor
followedByThisInterceptor
thisWillRunLastInterceptor
MyAction1
MyAction2 (chain)
MyPreResultListener
MyResult (result)
thisWillRunLastInterceptor
followedByThisInterceptor
thisWillRunNextInterceptor
thisWillRunFirstInterceptor
2 使用默认拦截器配置每个Action都需要的拦截器堆栈,例如:
<action name="login" class="tutorial.Login">
<interceptor-ref name="timer"/>
<interceptor-ref name="logger"/>
<interceptor-ref name="default-stack"/>
<result name="input">login.jsp</result>
<result type="redirect-action">/secure/home</result>
</action>
可以按照如下的方式定义:
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="timer"/>
<interceptor-ref name="logger"/>
<interceptor-ref name="default-stack"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack"/>
<action name="login" class="tutorial.Login">
<result name="input">login.jsp</result>
<result type="redirect-action">/secure/home</result>
</action>
3 如何访问HttpServletRequest,HttpServletResponse或者HttpSession
有两种方法可以达到效果,使用ActionContext:
Map attibutes = ActionContext.getContext().getSession();
或者实现相应的接口:
HttpSession SessionAware
HttpServletRequest ServletRequestAware
HttpServletResponse ServletResponseAware
Struts2自定义拦截器实例—只允许从登录页面进入系统
【1】struts.xml:
<!-- 定义一个拦截器 -->
<interceptors>
<interceptor name="authority"
class="org.interceptot.LoginInterceptor">
</interceptor>
<!-- 拦截器栈 -->
<interceptor-stack name="mydefault">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="authority" />
</interceptor-stack>
</interceptors>
<!-- 定义全局Result -->
<global-results>
<!-- 当返回login视图名时,转入/login.jsp页面 -->
<result name="login">/login.jsp</result>
</global-results>
<action name="show" class="org.action.showAction">
<result name="success">/main.jsp</result>
<!-- 使用此拦截器 -->
<interceptor-ref name="mydefault" />
</action>
<!--验证登录用户信息 -->
<action name="login" class="org.action.loginAction" method="execute">
<result name="error">/login.jsp</result>
<result name="input">/login.jsp</result>
</action>
【2】自定义拦截器org.interceptot.LoginInterceptor:
package org.interceptot;
import java.util.Map;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class LoginInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// 取得请求相关的ActionContext实例
ActionContext ctx = invocation.getInvocationContext();
Map session = ctx.getSession();
String user = (String) session.get("username");
// 如果没有登陆,即用户名不存在,都返回重新登陆
System.out.println("user:"+user);
if (user != null) {
System.out.println("test");
return invocation.invoke();
}
System.out.println("你还没有登录");
ctx.put("tip", "你还没有登录");
return Action.LOGIN; //返回一个叫login的result结果
}
}
【3】进入主页面的Action:org.action.showAction
package org.action;
import com.opensymphony.xwork2.ActionSupport;
public class showAction extends ActionSupport {
public String execute() {
return "success";
}
}
【4】LoginAction:
private boolean isInvalid(String value) {
return (value == null || value.length() == 0);
}
if (isInvalid(user.getUsername()))
return INPUT;
if (isInvalid(user.getPassword()))
return INPUT;
//登录成功将User放入session中
HttpServletRequest request = ServletActionContext.getRequest();
Map map=ActionContext.getContext().getSession();
map.put("username", user.getUsername());
【5】如果我们通过show.action访问main.jsp那么就会被自定义拦截器拦住,拦截器检查session中
是否有值,有证明用户已经登录,没有则为没有登录,那么就会被跳转到登陆页面。


浙公网安备 33010602011771号