- 所谓的拦截器指的就是AOP(面向切片编程)的一种实现,就是代理设计模式的实现.
认识拦截器
- 在整个Struts2.x开发框架中,所有的用户请求都交给了Filter,这个FIlter要将请求交个指定的Action进行处理,而且一定要在Filter实现数据的转换处理.
- 所以在现在的框架设计过程之中,往往会在请求真正的达到摸一个Action之前做一些处理操作,而这就属于拦截器的功能.
-
在拦截器存在的前提下,才可以进行数据的验证操作.由于Struts2x使用的是过滤器接受的所有请求,这样也会造成一个问题,session登录检测不可能只在过滤器里处理.
-
定义一个简单的拦截器
- 拦截器的存在一定要附加在Action上
- 编写一个Action程序类
package mao.shu.action;
import com.opensymphony.xwork2.ActionSupport;
public class DemoAction extends ActionSupport{
public void add(){
System.out.println("**************执行add方法*****************");
}
}
- 配置struts.xml文件
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<package name="root" namespace="/" extends="struts-default">
<action name="DemoAction" class="mao.shu.action.DemoAction">
<interceptor-ref name="timer"></interceptor-ref><!-- 使用struts2内置的时间拦截器-->
</action>
</package>
</struts>
-
在第五行之中,使用了一个Struts2内置的时间拦截器,用于计算Action类方法执行的时间.
-
启动tomcat发布项目,执行http://localhost:8080/InterceptorProject/DemoAction!add.action
-
后台输出,可以发现输出的信息为,执行DempAction的add方法共使用了7毫秒
开发自定义的拦截器
- 如果要实现自定义的拦截器需要有一个专门的拦截器处理类.这个类要求必须有一个父类------AbstractInterceptor
- 该类定义如下
- 而在AbstractInterceprot类中定义有如下的三个方法.
方法名称 | 作用 |
---|---|
public void init() | 初始化拦截器操作 |
public void destroy() | 销毁拦截器 |
public abstract String intercept(ActionInvocation invocation) throws Exception | 子类复写该方法实现拦截处理 |
- 在这个类中定义的intercept()方法是一个抽象方法,这个方法必须有子类所复写,抽象方法中接受有一个ActionInvocation参数.在这个接口中提供一个请求向下处理的方法
方法名称 | 作用 |
---|---|
String invoke()throws Exception | 调用下一个拦截器,如果没有下一个拦截器,则执行当前的invoke()操作 |
- 定义一个简单的拦截器
package mao.shu.Interceptor;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class MyInteceptor extends AbstractInterceptor {
@Override
public void init() {
System.out.println("**********************"+this+"拦截器初始化开始********");
}
@Override
public String intercept(ActionInvocation arg0) throws Exception {
System.out.println("*****************invoke()方法执行前*************");
ServletActionContext.getRequest().setCharacterEncoding("UTF-8");
String result = arg0.invoke();
System.out.println("*****************invoke()方法执行后返回"+result+"*************");
ServletActionContext.getResponse().setCharacterEncoding("UFT-8");
return result;
}
@Override
public void destroy() {
System.out.println("**********************"+this+"拦截器销毁********");
}
}
-
拦截器定义完成之后如果要想使用,必须通过struts.xml文件进行处理
-
修改strtus.xml文件
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<package name="root" namespace="/" extends="struts-default">
<interceptors><!-- 声明自定义的拦截器-->
<interceptor name="MyInterceptor" class="mao.shu.Interceptor.MyInteceptor"></interceptor>
</interceptors>
<action name="DemoAction" class="mao.shu.action.DemoAction">
<interceptor-ref name="timer"></interceptor-ref><!-- 使用struts2内置的时间拦截器-->
<interceptor-ref name="MyInterceptor"/><!-- 引用拦截器-->
</action>
</package>
</struts>
-
拦截器将按照既定的顺序执行.
-
执行
-
启动tomcat
-
执行之后发现,默认情况下 容器一档启动之后拦截器会自动进行启动.
-
执行http://localhost:8080/InterceptorProject/DemoAction!add.action
-
此时的返回结果为null,因为此时的add()方法没有返回值,如果此时修改add()方法,使其有返回值
package mao.shu.action;
import com.opensymphony.xwork2.ActionSupport;
public class DemoAction extends ActionSupport{
public String add(){
System.out.println("**************执行add方法*****************");
return "forward.page";
}
}
- 再次请求执行add()方法后,返回结果变为了forward.page,
- 每个Action中的返回值,最终实际上就是拦截器中执行invoke()方法的返回结果.
自定义拦截器深入
-
拦截器是在过滤器和Action之间的产物,也就是说拦截器可以控制进入到Action中的数据是否正确,于是此时如果配置不当,就有可能出现偏差.
-
定义一个Message类
package mao.shu.vo;
public class Message {
private Integer mid;
private String info;
public Integer getMid() {
return mid;
}
public void setMid(Integer mid) {
this.mid = mid;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Message [mid=" + mid + ", info=" + info + "]";
}
}
- 随后在MessageAction里面希望可以进行内容的自动设置
- 定义MessageAction程序类
package mao.shu.action;
import com.opensymphony.xwork2.ActionSupport;
import mao.shu.vo.*;
public class MessageAction extends ActionSupport{
private Message msg = new Message();
public void setMsg(Message msg) {
this.msg = msg;
}
public Message getMsg() {
return msg;
}
public String add(){
System.out.println("新对象实例化成功********");
System.out.println(this.msg);
return null;
}
}
- struts.xml稳健的配置
<action name="MessageAction" class="mao.shu.action.MessageAction">
<interceptor-ref name="timer"></interceptor-ref><!-- 使用struts2内置的时间拦截器-->
</action>
- 直接诶利用地址重写的方式实现内容传递
http://localhost:8080/InterceptorProject/MessageAction!add.action?msg.mid=1,msg.info=你好
- 后台输出的结果
- 此时发现使用了拦截器并没有成功为属性设置该有的内容,因为没有使用设置内容的拦截器.
- 当使用了自定义的拦截器,系统中自定义的拦截器就会失效了.
- 因此要在struts.xml文件中添加系统自定义的拦截器
<action name="MessageAction" class="mao.shu.action.MessageAction">
<interceptor-ref name="defaultStack"></interceptor-ref><!-- 使用struts2默认的拦截器-->
<interceptor-ref name="timer"></interceptor-ref><!-- 使用struts2内置的时间拦截器-->
</action>
- 修改之后执行结果
- 属性设置
- 拦截器本身也属于一个java程序类,那么就有可能会有类属性.
- 如果拦截器之中需要接收一些属性的话,那么可以通过配置的参数完成.
- 在拦截器中定义属性
- 修改MyIinterceptor程序类,添加两个类属性,以及setter()方法
private String paramA ;
private String paramB;
public void setParamA(String paramA) {
this.paramA = paramA;
}
public void setParamB(String paramB) {
this.paramB = paramB;
}
- 所有的属性都可以通过配置文件处理,修改struts.xml文件
<action name="MessageAction" class="mao.shu.action.MessageAction">
<interceptor-ref name="MyInterceptor"><!-- 引用自定义拦截器-->
<param name="paramA">Hello</param><!-- 配置拦截器中的参数-->
<param name="paramB">World</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref><!-- 使用struts2默认的拦截器-->
<interceptor-ref name="timer"></interceptor-ref><!-- 使用struts2内置的时间拦截器-->
</action>
- 在MyInterceptor拦截器程序中的intercept()方法中输出拦截器的参数
System.out.println(this.paramA+":"+this.paramB);
- 执行MessageAction类中的add()方法的请求
- 改变返回模式
- 默认情况下拦截器会将invoke(0的内容返回,实际上这个内容可以自己定义
- 修改MyInterceptor拦截器中的intercept()方法的返回值
return "error.page";
-
实际上在开发之中拦截器一定是被所有的Action所使用,如果只是用某一个具体Action定义的"result"是非常不合理的,所以最好的做法是将其统一跳转到全局路径上
-
在struts.xml文件中配置全局跳转路径
-
该声明必须生命在拦截器之后
<global-results><!-- 拦截器统一的跳转路径-->
<result name="error.page">/error.page</result>
</global-results>
利用拦截器检测登录
-
在任何的项目之中都一定需要有登录验证,可是对于Struts2.x而言,所有的Action如果要想进行登录检测,就必须使用拦截器完成,过滤器只能够过滤*.jsp,*.html,登录文件路径等信息.
-
如果要想在拦截器中取的session属性,那么可以使用两种方案
- ServletActionContext.getReqeust().getSession().get()
- 通过ActionContext类取得session属性
-
编写一个LoginInterceptor拦截器
package mao.shu.Interceptor;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class LoginInterceptor extends AbstractInterceptor {
private String key;//保存登录信息的key
public void setKey(String key) {
this.key = key;
}
@Override
public String intercept(ActionInvocation arg0) throws Exception {
ActionContext actionContext = arg0.getInvocationContext();
String result = (String) actionContext.get(this.key);//根据key取得属性
if(result == null){
ServletActionContext.getRequest().setAttribute("msg", "你还未登录");
return "login";
}else{
return arg0.invoke();//如果查询到登录信息,则执行请求Action的方法
}
}
}
- 在struts.xml文件中设置新的拦截器
<interceptors><!-- 声明自定义的拦截器-->
<interceptor name="MyInterceptor" class="mao.shu.Interceptor.MyInteceptor"></interceptor>
<interceptor name="LoginInterceptor" class="mao.shu.Interceptor.LoginInterceptor"></interceptor>
</interceptors>
- 配置拦截器统一跳转到"login.page"路径
<global-results><!-- 拦截器统一的跳转路径-->
<result name="login">/login.jsp</result>
</global-results>
- 在MessageAction的配置中,使用该拦截器,并设置属性"key"的参数内容,假设登录信息使用"uid"为名称保存
<action name="MessageAction" class="mao.shu.action.MessageAction">
<interceptor-ref name="defaultStack"></interceptor-ref><!-- 使用struts2默认的拦截器-->
<interceptor-ref name="LoginInterceptor">
<param name="key">uid</param><!-- 设置拦截器属性内容-->
</interceptor-ref>
</action>
- 定义/login.jsp页面,登录的本质就是在用户输入正确的信息之后,将用户的登录信息保存在session属性之中,所以只为了模拟一个登录检测这个login.jsp页面只需要在页面之中设置一个session属性即可
- 表示只有访问过这个页面才算是登录过.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
session.setAttribute("uid", "xiemaoshu");
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>${msg} </h1>
</body>
</html>
- 测试:直接访问MessageAction中的add()方法的请求
- http://localhost:8080/InterceptorProject/MessageAction!add.action
拦截器栈
- 所谓的拦截器栈指的是配置一族拦截器为统一的名字,而在要使用的位置直接使用拦截器栈的名字即可操作.它的定义同样要在struts.xml文件中完成.
<interceptors><!-- 声明自定义的拦截器-->
<interceptor name="MyInterceptor" class="mao.shu.Interceptor.MyInteceptor"></interceptor>
<interceptor name="LoginInterceptor" class="mao.shu.Interceptor.LoginInterceptor"></interceptor>
<!-- 声明拦截器栈-->
<interceptor-stack name="maoshuStack">
<interceptor-ref name="MyInterceptor"/>
<interceptor-ref name="LoginInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
- 在action配置的时候,只需要使用拦截器栈即可
<action name="MessageAction" class="mao.shu.action.MessageAction">
<interceptor-ref name="maoshuStack">
<param name="key">uid</param><!-- 设置拦截器属性内容-->
</interceptor-ref>
<result name="success">/welcome.jsp</result>
</action>
- 以后开发中,往往会将许多的拦截器定义在拦截器栈之中.