拦截器杂谈
截拦器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,停止拦截然后在之前或以后加入某些操作。拦截器是AOP的一种实现策略。
拦截器是动态拦截Action调用的对象。它供给了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是供给了一种可以提取action中可重用的部分代码的方式。
Struts2很多核心的功能都是拦截器来实现!如:从请求中把请求参数封装到action、文件上传和数据验证等等都是通过拦截器实现的。
拦截器栈(Interceptor Stack)相似于过滤器链。拦截器栈就是将拦截器按必定的顺序联结成一条链。在拜访被拦截的方法或字段时,拦截器栈的拦截器就会按其之前定义的顺序被调用。
也可以叫做拦截器链(Interceptor Stack),拦截器栈一词更确明的表名了拦截器链的实现式方。
拦截器的工作流程
如下图所示:
工作流程大致如下:
1.启动Tomcat服务器,加载web.xml,初始化过滤器(StrutsPrepareAndExecuteFilter)
2.创建拦截器实例(构造器),初始化每个拦截器init()。
3.当用户请求访问,Struts找到指定哪个action,创建Action实例
4.执行第一个拦截器的interceptor()方法,执行一些业务操作之后,调用invoke()方法
5.如果有下一个拦截器,继续执行interceptor(),直到没有,执行action的execute()方法,处理请求,并返回return 值。
6.返回值依次经过拦截器,最终将响应发送给浏览器。
一个小面试题
先执行action类创建,还是先执行拦截器?
答:先执行Action类的创建,再执行拦截器; 然后拦截器inteceptor()业务处理执行完,再执行action中的业务方法。
自定义拦截器
因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能。
如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-ref name=“permissionStack”/>
把拦截器定义为默认拦截器。注意:每个包只能指定一个默认拦截器。另外,一旦我们为该包中的某个action显式指定了某个拦截器,则默认拦截器不会起作用。
自定义拦截器代码
注意:——–配置中
自定义的拦截器的引用一定要在默认拦截器的下面!
com.opensymphony.xwork2.interceptor.Interceptor
阅读该接口API,所有的自定义拦截器都需要实现该接口,且以单例运行。
HelloInterceptor.java
/**
* 自定义拦截器
*
*/
public class HelloInterceptor implements Interceptor{
// 服务器启动时候执行
public HelloInterceptor(){
System.out.println("创建了拦截器实例");
}
// 启动时候执行
@Override
public void init() {
System.out.println("执行了拦截器的初始化方法");
}
// 拦截器业务处理方法 (在访问action时候执行? 在execute之前执行?)
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("2. 拦截器,业务处理-开始");
// 调用下一个拦截器或执行Action (相当于chain.doFilter(..)
// 获取的是: execute方法的返回值
String resultFlag = invocation.invoke();
System.out.println("4. 拦截器,业务处理-结束"+resultFlag);
return resultFlag;
}
@Override
public void destroy() {
System.out.println("销毁....");
}
}
struts.xml
/**
* 自定义拦截器
*
*/
public class HelloInterceptor implements Interceptor{
// 服务器启动时候执行
public HelloInterceptor(){
System.out.println("创建了拦截器实例");
}
// 启动时候执行
@Override
public void init() {
System.out.println("执行了拦截器的初始化方法");
}
// 拦截器业务处理方法 (在访问action时候执行? 在execute之前执行?)
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("2. 拦截器,业务处理-开始");
// 调用下一个拦截器或执行Action (相当于chain.doFilter(..)
// 获取的是: execute方法的返回值
String resultFlag = invocation.invoke();
System.out.println("4. 拦截器,业务处理-结束"+resultFlag);
return resultFlag;
}
@Override
public void destroy() {
System.out.println("销毁....");
}
}
在启动服务器之后,控制台将会打印:
创建了拦截器实例
执行了拦截器的初始化方法
在用户请求访问HelloAction时,会打印
2. 拦截器,业务处理-开始
如果有多个拦截器,将会执行多个拦截器业务方法,然后会执行action中的业务方法。最后action返回标记如:
String resultFlag = invocation.invoke();
最后打印:4. 拦截器,业务处理-结束。
当服务器关闭时,打印 销毁….
注意—–ActionInvocation: 代表一个给定Action的执行状态, 拦截器可以从该类的对象里获得与该Action相关联的 Action 对象和 Result 对象. 在完成拦截器自己的任务之后, 拦截器将调用 ActionInvocation 对象的 invoke 方法前进到 Action 处理流程的下一个环节.
拦截器案例–简单登录拦截器
功能描述:当用户访问login.jsp登录页面时,拦截器直接放行,当用户访问list.jsp列表页面时,拦截器执行拦截,需要判断该用户是否已经登录,如果登录,直接放行,如果没有登录,则跳转到login.jsp登录页面。其中用户登录之后将用户信息保存到session中。
代码如下:
login.jsp
<body>
<form method="post" action="${pageContext.request.contextPath }/user_login.action">
用户名:<input type="text" name="admin.userName"><br/>
密码:<input type="text" name="admin.pwd"><br/>
<input type="submit" value="登陆"><br/>
</form>
</body>
UserAction.java
public class UserAction extends ActionSupport {
// ---------1. 自动封装请求数据-----------
private Admin admin;
public Admin getAdmin() {
return admin;
}
public void setAdmin(Admin admin) {
this.admin = admin;
}
// ---------2. 调用的Service(没用注入)-----------
private AdminService adminService = new AdminService();
// 用户登陆
public String login() {
try {
Admin userInfo = adminService.login(admin);
// 判断是否有该用户
if (userInfo == null){
// 登陆失败
return "input";
}
// 登陆成功:将用户保存在session中
ActionContext.getContext().getSession().put("userInfo", userInfo);
// 登陆成功
return "loginSuccess";
} catch (Exception e) {
return ERROR;
}
}
// 列表显示所有的用户数据
public String list() {
try {
// 查询全部
List<Admin> list = adminService.getAll();
// 保存到request
ActionContext.getContext().getContextMap().put("listAdmin", list);
return "list";
} catch (Exception e) {
return ERROR;
}
}
public String add() {
return null;
}
}
list.jsp
<body>
<h1>欢迎你,${userInfo.userName }</h1>
<table align="center" border="1">
<tr>
<td>序号</td>
<td>编号</td>
<td>用户名</td>
<td>密码</td>
</tr>
<%--@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" --%>
<!-- 用struts标签迭代数据 -->
<%@taglib uri="/struts-tags" prefix="s" %>
<s:iterator var="admin" value="#request.listAdmin" status="st">
<tr>
<td>
<s:property value="#st.count"/>
</td>
<td>
<s:property value="#admin.id"/>
</td>
<td>
<s:property value="#admin.userName"/>
</td>
<td>
<s:property value="#admin.pwd"/>
</td>
</tr>
</s:iterator>
</table>
</body>
自定义拦截器UserCheckInterceptor.java
public class UserCheckInterceptor extends AbstractInterceptor{
/**
* 拦截器业务处理方法
*/
public String intercept(ActionInvocation invocation) throws Exception {
// 拿到当前执行的方法名:判断,只有当前方法名不是login,就进行验证
//不登录不能访问list.jsp页面
// 获取ActionContext对象
ActionContext ac = invocation.getInvocationContext();
// 获取action的代理对象
ActionProxy proxy = invocation.getProxy();
// 获取当前执行的action中的方法名
String methodName = proxy.getMethod();
// 判断
if (!"login".equals(methodName)) {
// 先获取当前登陆的用户
Object obj = ac.getSession().get("userInfo");
if (obj == null) {
// 没有登陆
return "input";
} else {
// 当前用户有登陆
return invocation.invoke();
}
} else {
// 说明当前用户正在登陆
return invocation.invoke();
}
}
}
配置拦截器 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>
<package name="user" extends="struts-default">
<!-- 【拦截器配置】 -->
<interceptors>
<interceptor name="loginCheck" class="UserCheckInterceptor"></interceptor>
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="loginCheck"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 【执行拦截器:第一种写法: 当前包下所有的acntion都执行myStack栈】
<default-interceptor-ref name="myStack"></default-interceptor-ref>
-->
<!-- 全局配置 -->
<global-results>
<result name="error">/error.jsp</result>
</global-results>
<action name="user_*" class="UserAction" method="{1}">
<!--第二种写法: 只是在这一个Action中执行myStack栈
<interceptor-ref name="defaultStackt"></interceptor-ref>
<interceptor-ref name="loginCheck"></interceptor-ref>
-->
<!-- 第三种写法:执行用户栈(与第二种写法一样, 只在当前aciton中执行自定义栈) -->
<interceptor-ref name="myStack"></interceptor-ref>
<!-- 1. 登陆失败 -->
<result name="input">/login.jsp</result>
<!-- 2. 登陆成功 -->
<result name="loginSuccess" type="redirectAction">user_list</result>
<!-- 3. 列表展示 -->
<result name="list">/WEB-INF/list.jsp</result>
</action>
</package>
</struts>
小面试题之二
过滤器和struts2拦截器的区别?
答:
都是对于用户的请求起到拦截的作用,只不过Filter是Java EE规范的概念,而拦截器Interceptors
是Struts2中提出的概念! Struts2中过滤器负责调用拦截器!
拦截器与过滤器的区别 :
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调
2、过滤器依赖与servlet容器,而拦截器不依赖与servlet容器
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次