一、理解Struts2拦截器
1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.
2. 拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
二、实现Struts2拦截器原理
Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器
三、定义Struts2拦截器。
Struts2规定用户自定义拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口。该接口声明了3个方法,
void init();
void destroy();
String intercept(ActionInvocation invocation) throws Exception;
其中,init和destroy方法会在程序开始和结束时各执行一遍,不管使用了该拦截器与否,只要在struts.xml中声明了该Struts2拦截器就会被执行。
intercept方法就是拦截的主体了,每次拦截器生效时都会执行其中的逻辑。
不过,struts中又提供了几个抽象类来简化这一步骤。
public abstract class AbstractInterceptor implements Interceptor;
public abstract class MethodFilterInterceptor extends AbstractInterceptor;
都是模板方法实现的。
其中AbstractInterceptor提供了init()和destroy()的空实现,使用时只需要覆盖intercept()方法;
而MethodFilterInterceptor则提供了includeMethods和excludeMethods两个属性,用来过滤执行该过滤器的action的方法。可以通过param来加入或者排除需要过滤的方法。
1.interceptor的配置
拦截器的使用:1.先定义;2.在引用使用;
//定义拦截器
<interceptor name="myInterceptor" class="com.zzz.struts2.interceptor.MyInterceptor">
//引用使用
<interceptor-ref name="myInterceptor">
</interceptor-ref>
各种引用使用如下图:
方法1. 普通配置法
<struts>
<package name="struts2" extends="struts-default">
<interceptors>
<interceptor name="myInterceptor" class="edu.hust.interceptor.MyInterceptor"></interceptor>
</interceptors>
<action name="register" class="edu.hust.action.RegisterAction">
<!-- 在自定义interceptor并将其ref时, 系统会覆盖掉默认的interceptor-stack(defaultStack), 为了保证系统默认的defaultStack不受影响, 我们需要显式的将其引入 -->
<!-- 注意两个interceptor-ref的顺序, 顺序不同, 执行效果也不同: 先配置的先执行/后配置的先退出(先进后出) -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="myInterceptor"></interceptor-ref>
<result name="input">/register.jsp</result>
<result>/result.jsp</result>
</action>
</package>
</struts>
[size=medium]
注意:
<package name="struts-shop" extends="struts-default">
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="checkbox">
<param name="uncheckedValue">0</param>
</interceptor-ref>
[color=red]<interceptor-ref name="defaultStack"/> </interceptor-stack>[/color]
</interceptors>
<default-interceptor-ref name="myStack"/>(这句是设置所有Action自动调用的拦截器堆栈)
</package>
<interceptor-ref name="defaultStack"/> </interceptor-stack>
在拦截器站中引用,或者在action中引用,必须得有,否则出错
方法2. 配置拦截器栈(即将多个interceptor串联的一种元素)。然后在<action>中引入该拦截器栈就可以了。[/size]
<struts>
<package name="struts2" extends="struts-default">
<interceptors>
<interceptor name="myInterceptor" class="edu.hust.interceptor.MyInterceptor"></interceptor>
<interceptor-stack name="myInterceptorStack">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="register" class="edu.hust.action.RegisterAction">
<result name="input">/register.jsp</result>
<result>/result.jsp</result>
<interceptor-ref name="myInterceptorStack"></interceptor-ref>
</action>
</package>
</struts>
方法3. 修改默认拦截器,将自定义的拦截器栈定义为struts2的默认拦截器。
<struts>
<package name="struts2" extends="struts-default">
<interceptors>
<interceptor name="myInterceptor" class="edu.hust.interceptor.MyInterceptor"></interceptor>
<interceptor-stack name="myInterceptorStack">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 此默认interceptor是针对所有action的 -->
<!-- 如果某个action中引入了interceptor, 则在这个action中此默认interceptor就会失效 -->
<default-interceptor-ref name="myInterceptorStack"></default-interceptor-ref>
<action name="register" class="edu.hust.action.RegisterAction">
<result name="input">/register.jsp</result>
<result>/result.jsp</result>
</action>
</package></struts>
拦截器接口interceptor 和抽象类AbstracctInterceptor应用
package com.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class MyInterceptor implements Interceptor{
private String hello;//一定要写,后面会用上
get和set方法
public void init() {
System.out.println("init");
}
public String intercept(ActionInvocation invoker) throws Exception {
System.out.println("intercept");
String result=invoker.invoke();
return result;
}
public void destroy() {
System.out.println("destory");
}
}
我们在做拦截器的时候,实现了Interceptor接口,里面有三个方法,但是一般的情况下init() 和 destroy() 方法我们用不上,最关心的就是intercept(ActionInvocation invoker){}方法,所以怎么办呢?其实,struts2给我们提供了一个简化的拦截器类:AbstractInterceptor
AbstractInterceptor 这是一个抽象的类,里面实现了 init() 和 destroy() 方法,所以只要我们继承这个类,就不用再多写这两个方法!
所有拦截器的超级接口Interceptor ,Action去实现这个接口;
Interceptor 它其中有三个方法(init(),destroy() ,interceptor()):
Init()方法:在服务器起动的时候加载一次,并且只加载一次;
Destroy()方法:当拦截器销毁时执行的方法;
Interceptor()方法:其中里边有一个参数invocation
显然intercept()方法,是添加真正执行拦截工作的代码的地方,如果我们不需要初始化和清理的操作,可以直接继承com.opensymphony.xwork2.interceptor.AbstractInterceptor类,覆盖它的intercept()方法。这个方法有个ActionInvocation类型的参数,可以用它取得Session或转发请求等操作。
示例展示:
明白了原理之后还需要做几项具体的工作:
1,自己写一个拦截器,实现检查用户是否登陆的功能。
2,添加拦截器的配置,让我们自己写的拦截器起作用。
LogonInterceptor.java
package tutorial.interceptor;
import java.util.Map;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
/**
* 登陆验证拦截器,如果用户没有登陆系统则让用户到登陆验证页面进行登陆
* 当用户请求网站资源的时候,例如某个Action,在Action被执行之前,检
* 查用户是否登陆了系统.
* 不足之处是该拦截器只能拦截Action,不能拦截jsp和servlet,所以不能
* 完全限制用户对未授权资源的访问,可以配合filter或将jsp页面放在
* WEB-INF目录下解决这个问题.
*
* @author coombe
*/
public class LogonInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
// 取得请求的Action名
String name = invocation.getInvocationContext().getName();
if (name.equals("Login")) {
// 如果用户想登录,则使之通过
return invocation.invoke();
} else {
// 取得Session。
ActionContext ac = invocation.getInvocationContext();
Map session = (Map)ac.get(ServletActionContext.SESSION);
if (session == null) {
// 如果Session为空,则让用户登陆。
return "login";
} else {
String username = (String)session.get("username");
if (username == null) {
// Session不为空,但Session中没有用户信息,
// 则让用户登陆
return "login";
} else {
// 用户已经登陆,放行~
return invocation.invoke();
}
}
}
}
}
/*1,自定义拦截器
2,重定义默认拦截器堆栈
3,添加一个global-results,用户在验证失败的情况下跳转到登陆验证页面
*/
struts.xml
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts><!-- Configuration for the default package. -->
<constant name="struts.custom.i18n.resources" value="globalMessages" />
<include file="struts-validation.xml" />
<package name="default" extends="struts-default">
<!-- 自定义拦截器 -->
<interceptors>
<interceptor name="logon" class="tutorial.interceptor.LogonInterceptor"/>
<!-- 自定义拦截器堆栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="logon"/>
<!-- 引用默认的拦截器堆栈
如果不用defaultStack,则表单值将会被页面跳转后返回-->
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 重定义默认拦截器堆栈 -->
<default-interceptor-ref name="myStack"/>
<global-results>
<result name="login" type="redirect-action">Login!input.action</result>
</global-results>
<action name="HelloWorld" class="tutorial.HelloWorld">
<result>/HelloWorld.jsp</result>
</action>
<action name="Login" class="tutorial.Login">
<result>/Success.jsp</result>
<result name="input">/Login.jsp</result>
</action>
</package>
</struts>
方法过滤拦截器MethodFilterInterceptor
1:上边的拦截器都要是针对整个action的,如果针对某个方法进行拦截可以去继承这个类;
它的使用跟上边的使用方法差不多,只是需要要配置它对那个方法进行拦截,方法过滤拦截器最好不要配置到自己设置默认的拦截器栈里边,自己手动配置.
<interceptor-ref name="myInterceptor3">
<param name="includeMethods">execute</param>
<param name="excludeMethods">execute</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
其中includeMethods ,excludeMethods是固定写法: includeMethods 包含拦截那些方法,多个方法需要用”,”隔开; excludeMehtods是排除拦截的那些方法;
拦截器添加参数
MyInterceptor.java
package com.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class MyInterceptor implements Interceptor{
private String hello;//一定要写,后面会用上
get和set方法
public void init() {
System.out.println("init");
}
public String intercept(ActionInvocation invoker) throws Exception {
System.out.println("intercept");
String result=invoker.invoke();
return result;
}
public void destroy() {
System.out.println("destory");
}
}
private String hello; get和set方法
写个变量,然后加上get和set方法,当然变量的名字必须和设定的参数是相同的,这个是赋值成功的前提条件
可以通过以下两种方式添加参数
1. 定义拦截器的时候添加参数:
Xml代码
<interceptor name="myinterceptor" class="com.interceptor.MyInterceptor">
<param name="hello">world</param>
</interceptor>
2. 使用拦截器的时候添加参数:
Xml代码
<interceptor-ref name="myinterceptor">
<param name="hello">zhuxinyu</param>
</interceptor-ref>
可是还有个问题,在定义的时候添加了参数hello,使用时同样添加了参数param,当运行MyInterceptor类时会输出哪个呢? world 还是 zhuxinyu。
结果是:zhuxinyu 很明显,覆盖了第一个,这是什么原则:就近原则。形如 OO 中的覆盖,重写
此时,运行MyInterceptor.java,在整个拦截器中任何方法中运行 System.out.println(hello);成功输出: world
Struts2拦截器的执行顺序
1.如果一个系统中配置了多个拦截器,根据拦截器配置的顺序不同,执行拦截器的顺序也不一样。通常认为,先配置的拦截器,会先获得执行的机会,但是实际情况不是这样。execute()方法执行之前,配置在前面的拦截器,会先对用户的请求起作用。execute()方法执行之后,配置在后面的拦截器,会先对用户的请求起作用。(两头向中间靠拢的执行)
定义拦截器
Java代码
package com.sh.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class DefaultInterceptor extends AbstractInterceptor{
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println(name+"执行了。。。");
String result=invocation.invoke();
System.out.println(name+"执行完毕。。。");
return result;
}
}
action
Java代码
package com.sh.action;
import com.opensymphony.xwork2.ActionSupport;
public class DefaultAction extends ActionSupport {
public String execute(){
return SUCCESS;
}
}
struts.xml
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配置改变时候不需要重新启动tomcate -->
<constant name="struts.devMode" value="false"/>
<constant name="struts.convention.classes.reload" value="true" />
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="inter1" class="com.sh.interceptor.DefaultInterceptor"/>
<interceptor name="inter2" class="com.sh.interceptor.DefaultInterceptor"/>
<interceptor name="inter3" class="com.sh.interceptor.DefaultInterceptor"/>
</interceptors>
<action name="default" class="com.sh.action.DefaultAction">
<result>/index.jsp</result>
<interceptor-ref name="inter1">
<param name="name">拦截器1</param>
</interceptor-ref>
<interceptor-ref name="inter2">
<param name="name">拦截器2</param>
</interceptor-ref>
<interceptor-ref name="inter3">
<param name="name">拦截器3</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
</action>
</package>
</struts>
--访问
http://localhost:8080/Struts2_interceptorSort/default.action
--控制台的信息 如下
拦截器1执行了。。。
拦截器2执行了。。。
拦截器3执行了。。。
拦截器3执行完毕。。。
拦截器2执行完毕。。。
拦截器1执行完毕。。。
[/size]