最近学习了一下Struts2,之前用过1,大致了解这个框架是什么东西,但是很少用到,现在有时间,正好学学它。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这一点跟1完全不一样,而且以职责链模式设计的拦截器可谓Struts2的基石,异常处理,国际化等等处理都在拦截器的实现中完成了。
Struts2支持多种表达式语言(OGNL,JSTL,Groovy还有Velocity,第一种是默认),印象中1中取值让人很头大,jsp中的代码也是乱的不行,到了2的时候,OGNL相当方便,一句<s:property value="xx" />就足矣了,然后再弄明白#、%和$这仨符号在什么情况下用,表达式这块就ok了,剩下不会的用到的时候看官方文档。
专注看了下拦截器的过程,一个请求首先经由应用服务器(本人用的是resin)的分配,根据web.xml中配置filter发到StrutsPrepareAndExecuteFilter.doFilter()中,然后方法中设置编码和国际化,创建上下文,分配派发线程,准备Action映射等一系列初始化操作之后,来到了要执行下一步的地方。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { prepare.setEncodingAndLocale(request, response); prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { request = prepare.wrapRequest(request); ActionMapping mapping = prepare.findActionMapping(request, response, true); if (mapping == null) { boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { execute.executeAction(request, response, mapping); // 这里将要执行下一步过程 } } } finally { prepare.cleanupRequest(request); } }
接下来在Dispatcher.serviceAction()中,会调用StrutsActionProxy.execute(),在这里面会调用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 { // interceptors是一个有序的拦截器列表,如果没有遍历完这个列表,程序将不会往下走,而是继续调用下一个拦截器的处理过程 // 在拦截器的intercept()方法中,会回调invocation.invoke()回到这个方法开始的时候,继续寻找下一个拦截器 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); } }
就像Struts2文档中的图一样,一层层的拦截器包围着待调用的action,等action返回之后,再一层层突破,实现双向拦截。
在debug过程中打上断点,可以清楚地看到整个过程
这就是一个请求被拦截器层层过滤的大致过程,同样也是一个典型的职责链模式的精巧实现