Struts2精萃之interceptor

下面这段话能完美诠释拦截器的含义:


拦截器是AOP中的概念,它本身是一段代码,可以通过定义“织入点”,来指定拦截器的代码在“织入点”的前后执行,从而起到拦截的作用。Struts2的Interceptor,其拦截的对象是Action代码,可以定义在Action代码之前或者之后执行拦截器的代码。


这是Struts2.3.4中Interceptor结构图:


25003651_DcQb.png


图中,我们可以发现,Struts2的Interceptor一层一层,把Action包裹在最里面。这样的结构,大概有以下一些特点:

1. 整个结构就如同一个堆栈,除了Action以外,堆栈中的其他元素是Interceptor

2. Action位于堆栈的底部。由于堆栈"先进后出"的特性,如果我们试图把Action拿出来执行,我们必须首先把位于Action上端的Interceptor拿出来执行。这样,整个执行就形成了一个递归调用

3. 每个位于堆栈中的Interceptor,除了需要完成它自身的逻辑,还需要完成一个特殊的执行职责。这个执行职责有3种选择:
1) 中止整个执行,直接返回一个字符串作为resultCode
2) 通过递归调用负责调用堆栈中下一个Interceptor的执行
3) 如果在堆栈内已经不存在任何的Interceptor,调用Action


一个典型的Interceptor的抽象实现类
public abstract class AroundInterceptor extends AbstractInterceptor {  
        @Override  
        public String intercept(ActionInvocation invocation) throws Exception {  
            String result = null;  
      
            before(invocation);  
            // 调用下一个拦截器,如果拦截器不存在,则执行Action  
            result = invocation.invoke();  
            after(invocation, result);  
      
            return result;  
        }  
          
        public abstract void before(ActionInvocation invocation) throws Exception;  
      
        public abstract void after(ActionInvocation invocation, String resultCode) throws Exception;  
      
    }


在这个实现类中,实际上已经实现了最简单的拦截器的雏形。或许大家对这样的代码还比较陌生,这没有关系。我在这里需要指出的是一个很重要的方法invocation.invoke()。这是ActionInvocation中的方法,而ActionInvocation是Action调度者,所以这个方法具备以下2层含义:

1. 如果拦截器堆栈中还有其他的Interceptor,那么invocation.invoke()将调用堆栈中下一个Interceptor的执行。

2. 如果拦截器堆栈中只有Action了,那么invocation.invoke()将调用Action执行。

所以,我们可以发现,invocation.invoke()这个方法其实是整个拦截器框架的实现核心。基于这样的实现机制,我们还可以得到下面2个非常重要的推论:

1. 如果在拦截器中,我们不使用invocation.invoke()来完成堆栈中下一个元素的调用,而是直接返回一个字符串作为执行结果,那么整个执行将被中止。

2. 我们可以以invocation.invoke()为界,将拦截器中的代码分成2个部分,在invocation.invoke()之前的代码,将会在Action之前被依次执行,而在invocation.invoke()之后的代码,将会在Action之后被逆序执行。

由此,我们就可以通过invocation.invoke()作为Action代码真正的拦截点,从而实现AOP。


Struts2的interceptor工作原理:
从源码中,我们可以看到,Struts2的Action层的4个不同的层次,他们分别是:拦截器(Interceptor)、Action、PreResultListener和Result。每个层次都具备了高度的扩展性和插入点,使得程序员可以在任何喜欢的层次加入自己的实现机制改变Action的行为。

interceptor是由actionInvocation驱动执行的。AroundInterceptor的子类会按照顺序被ActionInvocation调用执行。
intercept()方法对ActionInvocation的invoke()方法进行递归调用,ActionInvocation循环嵌套在intercept()中,一直到语句result = invocation.invoke()执行结束。这样,Interceptor又会按照刚开始执行的逆向顺序依次执行结束。

一个有序链表,通过递归调用,变成了一个堆栈执行过程,将一段有序执行的代码变成了2段执行顺序完全相反的代码过程,从而巧妙地实现了AOP。这也就成为了Struts2的Action层的AOP基础。

interceptor是一个栈结构,先进后出,递归与否与执行顺序无关。

当interceptor1执行调用interceptor2的时候,interceptor1并没有执行完毕,而是被暂时搁置了,因此等到action执行完毕的时候,必然是逆序执行之前被搁置的所有代码。
这是数据结构的知识。



下面是struts2.3.4中struts-default.xml里配置的interceptor:
拦截器名称
功能描述
alias
在不同请求之间将请求参数在不同名字间进行转换,请求内容不变
chain
让前一个Action的属性可以被后一个Action访问,需要和<result type=”chain”>结合使用
checkbox
添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox
cookie
使用配置的name,value来是指cookies
conversionError
将类型转换错误从ActionContext中取出,并转换成Action实例中FieldError错误
createSession
自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务
debugging
提供不同的调试用的页面来展现内部的数据状况。
execAndWait
在后台执行Action,同时将用户带到一个中间的等待页面。
exception
将捕捉到的异常信息输出到指定的异常显示页面
fileUpload
负责解析表单中文件域的内容从而提供文件上传功能
i18n
记录用户选择的locale到session中,从而实现消息资源的国际化
logger
记录日志输出Action的名字
store
存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等
modelDriven
如果一个类实现了ModelDriven,将getModel得到的结果压入ValueStack中
scopedModelDriven
如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用Action的setModel方法将其放入Action内部
params
将表单或者HTTP请求中的参数值赋给Action实例中相应的属性
prepare
如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法
scope
将Action状态存入session和application的简单方法
servletConfig
提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问
staticParams
从struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中
roles
确定用户是否具有JAAS指定的Role,否则不予执行
timer
输出Action执行的时间,在做Action性能分析时很有用
token
通过Token来避免双击
tokenSession
和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中
validation
使用action-validation.xml文件中定义的内容校验提交的数据
workflow
调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面
N/A
从参数列表中删除不必要的参数
profiling
通过参数激活profile
multiselect
像复选框checkbox拦截器一样,程序检测到没有值被选中,将没有选中的option的值设定为空的参数


下面是一个登录的interceptor:
package com.chou.jtsms.ui.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

/**
 * @author chou
 * @version 1.0 2012/04/21
 * 拦截除登录以外所有请求判断是否已经登陆过
 * 
 */
public class LoginInterceptor implements Interceptor {

	private static final long serialVersionUID = -3224907200600094135L;

	public void destroy() {

	}

	public void init() {

	}

	/**
	 * 如果是/login.action或者/login请求就跳转到LoginAction处理
	 * 如果不是登录请求就判断是否登陆过,如果登陆成功过还需要再验证一遍,通过namespace来判断该用户应有的权限
	 */
	public String intercept(ActionInvocation invocation) throws Exception {
		HttpServletRequest request = ServletActionContext.getRequest();
		HttpSession session = ServletActionContext.getRequest().getSession();
		String path = request.getServletPath();
		String[] namespaces = StringUtils.split(request.getRequestURI(), "/");
		String namespace = "";
		if (namespaces.length>1) {//截取的namespace应该除去项目名,所以至少数组长度为2
			namespace = namespaces[1];
		}
		if ("/login".equals(path) || "/login.action".equals(path)){
			return invocation.invoke();
		}else {
			UserDTO userDTO = (UserDTO) session.getAttribute("logininfo");
			if(userDTO == null){
				return "noSession";
			}
			if(userDTO.getCansend().equals("1")){//必须有发短信权限
				if(namespace.equals("admin") && userDTO.getKind().equals(UserDTO.ADMIN)){
					return invocation.invoke();
				}
				if(namespace.equals("safeadmin") && userDTO.getKind().equals(UserDTO.SAFE_ADMIN)){
					return invocation.invoke();
				}
				if(namespace.equals("safe") && userDTO.getKind().equals(UserDTO.SAFE_USER)){
					return invocation.invoke();
				}
				if(namespace.equals("common") && userDTO.getKind().equals(UserDTO.COMMON)){
					return invocation.invoke();
				}
			}
		}
		return "noSession";
	}

}


...
<interceptor name="authority" class="com.chou.jtsms.ui.interceptor.LoginInterceptor"/>
<interceptor name="crudLog" class="com.chou.jtsms.ui.interceptor.LogInterceptor" />

<interceptor-stack name="myInterceptor">
     <interceptor-ref name="authority" />
     <interceptor-ref name="paramsPrepareParamsStack" />
     <interceptor-ref name="crudLog" />
</interceptor-stack>

<default-interceptor-ref name="myInterceptor" />
...

转载于:https://my.oschina.net/easyean/blog/535123

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值