关于xwork(struts2)拦截器体系的思考与分析

 

在xwork它的AOP体系在整个框架中占有非常重要的部分,实现许多非常好用的功能,例如参数设置,文件上传,字段检验等等.在xwork中AOP提供服务的组件称为拦截器,它们对需要执行的Action与Result进行了拦截封装,这种包装形式比较死板,比起spring中的AOP模式,当然是没法比的,但在xwork这个具体框架中,这种拦截模式还是十分有意义的.所有的拦截器通过固定接口来进行定义,最终所有拦截器被串在一个链上,最后执行的Action与Result在链的末尾.在xwork中AOP实现并没有使用代理,而是通过一个称之为ActionInvocation对象来进行组织与实现的.关于拦截器包装Action与Result的一个大致流程我就不想说了,网上到处都是.

由于xwork的它并不是AOP框架,因此在xwork中的拦截体系只AOP设计模式的一个小小应用,不使用动态代理,我想可能使它效率会更高一些,实际这个拦截体系统的一个重要实现类为DefaultActionInvocation,它通过一个精心组织的方法交替调用来实现,具体的代码如下: 

    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 {
                resultCode = invokeActionOnly();
            }

            // this is needed because the result will be executed, then control will return to the Interceptor, which will
            // return above and flow through again
            if (!executed) {
                if (preResultListeners != null) {
                    for (Object preResultListener : preResultListeners) {
                        PreResultListener listener = (PreResultListener) preResultListener;

                        String _profileKey = "preResultListener: ";
                        try {
                            UtilTimerStack.push(_profileKey);
                            listener.beforeResult(this, resultCode);
                        }
                        finally {
                            UtilTimerStack.pop(_profileKey);
                        }
                    }
                }

                // now execute the result, if we're supposed to
                if (proxy.getExecuteResult()) {
                    executeResult();
                }

                executed = true;
            }

            return resultCode;
        }
        finally {
            UtilTimerStack.pop(profileKey);
        }
    }

由于使用方法的交替调用,使得Action整个执行流程的分析特别不容易理解,最关键的代码在if (interceptors.hasNext()) {....这个代码块(我将它称之为代码块1)与if (!executed) {这个代码块(我将它称之为代码块2)这个两个代码是理解整个运行流程的关键.

首先需要明确一点就是DefaultActionInvocation是有状态的,框架会为每一个请求创建一个调用上下文对象DefaultActionInvocation,它有一个重要的功能就是记录拦截器的调用情况,有哪些已经调用,有哪些还没有调用,这就是代码块1的判定依据.通常情况下DefaultActionInvocation的此方法会与每一个拦截器的interceptor方法进行交替的调用,但也不是绝对这样,有些特别拦截器也可以决定不调用后续的拦截器与最终的Action与Result对象,但这种情况比较少见.所以这里先分析这种最正常的情况.

正常情况下会由ActionProxy对象调用DefaultActionInvocation的invoke方法,也就是上面这个方法,紧接着会进入拦截器的调用(DefaultActionInvocation会将自己作为参数传递),页拦截器通常又会回调调用上下文的invoke方法.因此就形成一个如下的调用链:

invoke->interceptor-invoke->interceptor->......(没有拦截器了)...->resultCode = invokeActionOnly();->Result#execute....................返回到各个拦器

在上面的调用链中需要特别的注意:就是所有拦器的代码有可能都会被调用一遍,但是DefaultActionInvocation#invoke中的代码却存在大量的冗余,冗余在哪里呢,就在代码块2中的因为在这块代码第一次被执行后,它将会被打上已经执行的标记,以后就不会再执行了.因此这段代码最有可能被执行的地点为最后一个拦截器调用DefaultActionInvocation#invoke方法时被执行,之后控制权限将又依次返回了交替的interceptor与invoke方法,此时invoke的执行已经被太大意义,因为Result已经执行,其实对于大多数据拦截器也已经没有太大的意义,但这不是所有情况.只是说大多数情况下在action执行之前拦截器要做事情往往比之后要多.大多数在两个核心的对象执行之后主要做一些统计,日志之类的工作.实际你可以去查看所有拦截器,基本上实质有用的功能都是在Action与Result对象执行之前所做的,执行之后只做一些锦上添花的工作.

上面是正常流程下整个拦截器的运行流程,如果说出现异常那就不一样了此时的代码块2将很难得到执行,除非在某个拦截器中对异常进行了处理,并返回了一个有效的resultCode接着在调用此拦截器的上一个invoke方法中会使得代码块2得以执行,即Result对象得到执行.

如果想在Action执行完成之后,通过Action的执行情况,对返回的结果有所控制,你就必须注册PreResultListener监听器,注册的对象是DefaultActionInvocation,而注册的最佳地点一般是在一些拦截器中,比如DebuggingInterceptor

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值