以下代码简单模拟了通过struts2请求一个action时,struts2是怎样先执行其拦截器,然后再执行指定action的,通过此能初步理解struts2拦截器的执行原理。此代码还模拟展示了请求执行一个struts2 的 action的过程中当发生异常时,struts2是如何使用拦截器处理异常的。
代码没有完全按照struts2请求一个action时执行流,即先通过Dispatcher,利用actionConfig,ActionMapping,根据action名称查找对应的action,创建一个ActionProxy实例,然后执行其方法execute,execute方法再调用执行ActionInvocation的invoke方法(可以参考org.apache.struts2.dispatcher.Dispatcher的serviceAction方法)。这里只是简单创建了一个ActionProxy,一个ActionInvocation,调用ActionInvocation.invoke,模拟了struts2先执行拦截器,再执行action,然后再回来继续执行拦截器这个核心逻辑,尤其是在struts2对action调用执行过程中出现异常时,对弄清它处理异常的逻辑相当有帮助。
代码中,ActionInvocation的it成员变量初始化时保存了所有的拦截器(可以看做是一个拦截器栈)。从invoke方法的 interceptor.intercept(ActionInvocation.this) 可知,当执行一个action时,调用ActionInvocation的invoke方法,invoke方法内部迭代拦截器列表it,首先执行拦截器1,接着拦截器1又调回到invoke,invoke继续调用拦截器2,依次类推,直至拦截器按照入栈出栈的顺序执行完毕。invoke中对拦截器的调用,可以看做是拦截器调用拦截器的逻辑。
注意拦截器栈中的拦截器执行时,入栈出栈的执行轨迹,action是在最后一个拦截器出栈执行时被调用执行的:当最后一个拦截器出栈执行,调用到ActionInvocation的invoke方法,invoke方法这时才去调用执行action。action执行完返回,之前入栈的拦截器会依次出栈,从上次执行中断处继续执行(如果发现有异常抛上来,则只执行到中断处,执行finally)。即拦截器的执行顺序是,action执行前:1,2,3,action执行后:3,2,1。
根据拦截器栈的执行顺序,struts2把处理异常的拦截器放在栈顶,即action执行前第一个执行,action执行完毕返回或中间拦截器执行异常返回后作为最后一个拦截器执行。这样,无论是action执行异常,还是拦截器执行异常,异常抛出时按照拦截器栈执行轨迹,异常最终被抛至最后出栈执行的拦截器1中,即被专门处理异常的拦截器catch住。
struts2里面,这样的异常处理拦截器是 ExceptionMappingInterceptor,跟其他拦截器的区别是它会把异常catch住(其他的拦截器只负责把异常throw出去),并查找客户端在struts配置文件中配置好的exception-mapping(局部或全局),返回exception-mapping的result,这样的result一般是客户端定义跳转到错误页面去。你可以在自定义拦截器时放开ExceptionMappingInterceptor的对写日志的限制,允许在ExceptionMappingInterceptor捕获到异常时写日志,参考这里:
如果客户端没有定义exception-mapping,ExceptionMappingInterceptor会把异常throw出去。
模拟代码如下 :
public class Struts2ActionInvocationTest {
public static void main(String[] args)throws Exception{
List<Inteceptor> list = new ArrayList<Inteceptor>();
Interceptor1 interceptor1 = new Interceptor1();
list.add(interceptor1);
Interceptor2 interceptor2 = new Interceptor2();
list.add(interceptor2);
Interceptor3 interceptor3 = new Interceptor3();
list.add(interceptor3);
ActionInvocation a = new ActionInvocation();
MyAction action = new MyAction();
ActionProxy proxy = new ActionProxy(null,action);
a.init(list,proxy);
a.invoke();
}
//action定义
private static class MyAction{
public String execute()throws Exception{
System.out.println("All interceptors finished first time,Action execute here");
if(true){ //特地抛异常,搞清struts2的异常处理机制
throw new Exception("action execute error");
}
return "success";
}
}
//action代理定义
private static class ActionProxy{
private Object action;
private String method;
public ActionProxy(String method,Object action){
this.action = action;
if(method == null){
method = "execute";
}
this.method = method;
}
public Object getAction() {
return action;
}
public String getMethod() {
return method;
}
}
//action调用
private static class ActionInvocation{
private Iterator<Inteceptor> it;
private String resultCode;
private ActionProxy proxy;
public void init(List<Inteceptor> interceptors,ActionProxy proxy){
it = interceptors.iterator();
this.proxy = proxy;
}
public String invoke()throws Exception{
if(it.hasNext()){
Inteceptor interceptor = it.next();
try {
System.out.println(interceptor.toString().concat(" start........."));
resultCode = interceptor.intercept(ActionInvocation.this);
} finally {
System.out.println(interceptor.toString().concat(" finished,result = " + resultCode));
}
}else{ //the last interceptor execute here when invoke this method,
// then invoke action
resultCode = (String) this.invokeAction();
}
return resultCode;
}
public Object invokeAction()throws Exception{
try {
Method method1 = proxy.getAction().getClass().getMethod(proxy.getMethod());
return method1.invoke(proxy.getAction(),new Object[0]);
}catch (NoSuchMethodException ex){
throw new IllegalArgumentException("The " + proxy.getMethod() + "() is not defined in action " + proxy.getAction().getClass() + "");
}catch (InvocationTargetException ex) {
//InvocationTargetException已把cause转为null,这里拿出原ex
Throwable t = ex.getTargetException();
if(t instanceof Exception){
throw (Exception)t;
}else{
throw ex;
}
}
}
}
//拦截器定义
private interface Inteceptor{
public String intercept(ActionInvocation a)throws Exception;
}
private static class Interceptor1 implements Inteceptor{
@Override
public String intercept(ActionInvocation a) throws Exception{
try{
return a.invoke();
}catch (Exception ex){
System.err.println("exception handle by Interceptor1 :" + ex.getMessage());
}
return null;
}
public String toString(){
return "Interceptor1";
}
}
private static class Interceptor2 implements Inteceptor{
public String intercept(ActionInvocation a) throws Exception{
return a.invoke();
}
public String toString(){
return "Interceptor2";
}
}
private static class Interceptor3 implements Inteceptor{
public String intercept(ActionInvocation a) throws Exception{
return a.invoke();
}
public String toString(){
return "Interceptor3";
}
}
}
执行打印的结果:
Interceptor2 start.........
Interceptor3 start.........
All interceptors finished first time,Action execute here
Interceptor3 finished,result = null
Interceptor2 finished,result = null
Interceptor1 finished,result = null
exception handle by Interceptor1 :action execute error
注意红色字体部分,是调用执行action时发生异常,最终抛至拦截器1时被捕获。拦截器1被定义成负责异常处理的类。
下面是struts2的DefaultActionInvocation类部分源码,DefaultActionInvocation的invoke方法中,如果拦截器列表存在拦截器,会依次执行拦截器的intercept方法,而拦截器的intercept方法又会调用DefaultActionInvocation的invoke方法,继续执行列表的下一个拦截器。即以拦截器调用拦截器的方式,action执行完(或有异常),根据拦截器的执行顺序,依次出栈从上次执行中断处继续执行(如果发现有异常就从中断处返回,执行finally块):
//@see com.opensymphony.xwork2.DefaultActionInvocation#invoke
public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey);
if (executed) {
throw new IllegalStateException("Action has already executed");
}
//拦截器递归调用执行
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else { //最后一个拦截器执行完后,执行action
resultCode = invokeActionOnly();
}
...
}