首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Spring-AOP、Struts2拦截器、MyBatis Plugin兑现原理比较(二)

2012-11-18 
Spring-AOP、Struts2拦截器、MyBatis Plugin实现原理比较(二)Struts 2?Struts2是由StrutsPrepareAndExecuteF

Spring-AOP、Struts2拦截器、MyBatis Plugin实现原理比较(二)

Struts 2

?

Struts2是由StrutsPrepareAndExecuteFilter 的?doFilter 来处理每个HTTP请求的

?

由执行器的executeAction进入执行流程

execute.executeAction(request, response, mapping);
?

实际的执行是由dispatcher发起的

    public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {        dispatcher.serviceAction(request, response, servletContext, mapping);    }

?

在serviceAction中会由ActionProxyFactory根据URL请求和struts的配置 , 生成对应Action的ActionProxy 实例?

?

            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(                    namespace, name, method, extraContext, true, false);            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());            // if the ActionMapping says to go straight to a result, do it!            if (mapping.getResult() != null) {                Result result = mapping.getResult();                result.execute(proxy.getInvocation());            } else {                proxy.execute();            }
?

?

Action的执行是由proxy.execute()触发的

在execute() ? 拦截器和Action执行都是由invocation调度的

?

    public String execute() throws Exception {        ActionContext nestedContext = ActionContext.getContext();        ActionContext.setContext(invocation.getInvocationContext());        String retCode = null;        String profileKey = "execute: ";        try {            UtilTimerStack.push(profileKey);            retCode = invocation.invoke();        } finally {            if (cleanupContext) {                ActionContext.setContext(nestedContext);            }            UtilTimerStack.pop(profileKey);        }        return retCode;    }
?

下面我们来看一下invocation的核心函数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 {                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);        }    }

?

从InterceptorMapping 链中,也就是拦截器链中取出下一个InterceptorMapping?,拦截器被封装在里面

?

【对interceptors的初始化,也就是解析配置文件,加载所有的拦截器实例,在这里就不分析了】

?

 final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();

?

?

执行拦截器的拦截方法,参数是本身这个中央调度器

?

 interceptor.getInterceptor().intercept(DefaultActionInvocation.this);

?

?

再看一个拦截器的例子?LoggingInterceptor

?

?

? ? ? ? logMessage(invocation, START_MESSAGE); ? ?//记录开始

? ? ? ? String result = invocation.invoke();? ? ? //调度器 推进拦截器链

? ? ? ? logMessage(invocation, FINISH_MESSAGE); ? //记录结束

?

?

public class LoggingInterceptor extends AbstractInterceptor {    private static final Logger LOG = LoggerFactory.getLogger(LoggingInterceptor.class);    private static final String FINISH_MESSAGE = "Finishing execution stack for action ";    private static final String START_MESSAGE = "Starting execution stack for action ";    @Override    public String intercept(ActionInvocation invocation) throws Exception {        logMessage(invocation, START_MESSAGE);        String result = invocation.invoke();        logMessage(invocation, FINISH_MESSAGE);        return result;    }  ......}
??

在invocation.invoke()推进拦截器链时,又会从InterceptorMapping 链中,也就是拦截器链中取出下一个InterceptorMapping?,下一个拦截器被封装在里面。接着执行拦截器的拦截方法,参数是本身这个中央调度器。下一个拦截器中也会调用?invocation.invoke();推进拦截器链前进。

?

如果拦截器已经执行完了, invoke() 会执行? resultCode = invokeActionOnly(); 执行对应的Action,并得到返回值。

?

前面的多次invoke()其实是形成了一个递归,当LoggingInterceptor中的 invocation.invoke();返回后,后面的记录结束语句会才会执行。这样这个LoggingInterceptor就形式了一个环绕的拦截器。

?

这个递归的调用,自然就是形成了层层包裹的结构,但在包裹的前后是否要执行相应的逻辑,就要看具体需求了。

?


Spring-AOP、Struts2拦截器、MyBatis Plugin兑现原理比较(二)

?

从这里可以看出,对目标的拦截细节和责任链的推进要在拦截器中显式实现。

?

总结:

和MyBatis相比,虽然都是责任链的层层调用,链的推进和包裹的前后的执行逻辑,也要在实现中直接体现。

但这里的调度器封装了拦截器链,向下推进也都是在一个统一的逻辑中执行,并将这个中央调度器传递到每个拦截器中,以便拦截器控制推进逻辑。

而MyBatis是层层proxy,第次传给拦截器的调度器都是封闭了下一个目标,可能是一个proxy,也可能是target,责任链的推进,就是执行下一个对象的对应方法。拦截器没有被统一组织。

?

?

?

热点排行