Struts2源码学习(四)——Struts2处理Http请求
在经典组合三大框架Struts2,Hibernate,Spring框架中,Struts2作为MVC中的View框架,其主要的实现的功能就是处理Http请求。在这篇文章中,我们将从源码的角度,来了解Struts2是如何处理Http请求的。
在之前的文章介绍中,我们在对Struts2框架的分析中,一直都离不开的两大模块,第一是负责连接外部元素与Java对象的关系,第二则是作为Struts2基础的XWork框架,负责将请求分配给XWork框架中的不同元素进行处理。
事实上,Struts2处理Http请求时,也主要分成对Http预处理阶段以及XWork事件处理这两个阶段,
首先通过图片给Struts2处理流程有个大概的印象,接下来,我们通过源码的层面,来深入了解这个处理Http的流程。
学习过Struts2框架的都知道,Struts2的入口类是配置在web.xml文件中的StrutsPrepareAndExecuteFilter类,这个类继承自我们所熟知的Filter接口,之前我们分析过这个类中的init方法,而现在,我们将深入查看这个类的核心逻辑处理方法doFilter,
1 public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { 2 // 之前文章中分析过的Http预处理以及逻辑执行类 3 protected PrepareOperations prepare; 4 protected ExecuteOperations execute; 5 6 // 核心逻辑方法 7 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 8 HttpServletRequest request = (HttpServletRequest)req; 9 HttpServletResponse response = (HttpServletResponse)res; 10 11 try { 12 String uri = RequestUtils.getUri(request); 13 14 // 是否被过滤的URL 15 if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) { 16 LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri); 17 chain.doFilter(request, response); 18 } else { 19 LOG.trace("Checking if {} is a static resource", uri); 20 21 // 是否处理静态资源 22 boolean handled = this.execute.executeStaticResourceRequest(request, response); 23 if (!handled) { 24 LOG.trace("Assuming uri {} as a normal action", uri); 25 // 设置编码和Locale 26 this.prepare.setEncodingAndLocale(request, response); 27 // 创建ActionContext 28 this.prepare.createActionContext(request, response); 29 // 为当前线程分配Dispatcher 30 this.prepare.assignDispatcherToThread(); 31 // 封装 Request对象 32 request = this.prepare.wrapRequest(request); 33 // 根据request 查找 ActionMapping 34 ActionMapping mapping = this.prepare.findActionMapping(request, response, true); 35 if (mapping == null) { 36 LOG.trace("Cannot find mapping for {}, passing to other filters", uri); 37 chain.doFilter(request, response); 38 } else { 39 LOG.trace("Found mapping {} for {}", mapping, uri); 40 // 执行URL的处理 41 this.execute.executeAction(request, response, mapping); 42 } 43 } 44 } 45 } finally { 46 // 清理Request 47 this.prepare.cleanupRequest(request); 48 } 49 50 } 51 52 // 省略其他代码。,。。。 53 }
结合注释,整体的代码还是可以看得懂,其中的大部分方法最终都是对Dispatcher类的方法的调用。接下来对其中的方法进行深入了解。
(1)设置编码的setEncodingAndLocale方法,
1 public class PrepareOperations { 2 public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { 3 this.dispatcher.prepare(request, response); 4 } 5 6 。。。。。。 7 }
转为对Dispatcher方法的调用,
1 public class Dispatcher { 2 3 public void prepare(HttpServletRequest request, HttpServletResponse response) { 4 String encoding = null; 5 // 设置默认的 Encoding 6 if (this.defaultEncoding != null) { 7 encoding = this.defaultEncoding; 8 } 9 10 if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { 11 encoding = "UTF-8"; 12 } 13 // 设置 Locale 14 Locale locale = this.getLocale(request); 15 if (encoding != null) { 16 this.applyEncoding(request, encoding); 17 } 18 19 if (locale != null) { 20 response.setLocale(locale); 21 } 22 23 if (this.paramsWorkaroundEnabled) { 24 request.getParameter("foo"); 25 } 26 27 } 28 29 30 }
(2)创建ActionContext的方法createActionContext,
1 public class PrepareOperations { 2 3 public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) { 4 5 // 维护一个方便cleanup方法的计数器 6 Integer counter = Integer.valueOf(1); 7 Integer oldCounter = (Integer)request.getAttribute("__cleanup_recursion_counter"); 8 if (oldCounter != null) { 9 counter = oldCounter.intValue() + 1; 10 } 11 // 获取当前线程的 ActionContext 12 ActionContext oldContext = ActionContext.getContext(); 13 ActionContext ctx; 14 if (oldContext != null) { 15 // 若不为空, 复制成新的 ActionContext 16 ctx = new ActionContext(new HashMap(oldContext.getContextMap())); 17 } else { 18 // 若为空,创建 ValueStack,使用ValueStack的Context创建一个ActionContext 19 ValueStack stack = ((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack(); 20 stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null)); 21 ctx = new ActionContext(stack.getContext()); 22 } 23 24 request.setAttribute("__cleanup_recursion_counter", counter); 25 // ActionContext 设置到当前线程中 26 ActionContext.setContext(ctx); 27 return ctx; 28 } 29 、 30 。。。。。 31 }
(3)为当前线程分配Dispatcher的assignDispatcherToThread方法,
1 public class PrepareOperations { 2 3 public void assignDispatcherToThread() { 4 Dispatcher.setInstance(this.dispatcher); 5 } 6 7 }
又是调用Dispatcher中的方法,
1 public class Dispatcher { 2 3 public static Dispatcher getInstance() { 4 return (Dispatcher)instance.get(); 5 } 6 7 。。。。。。。 8 }
(4)封装HttpServletRequest的wrapRequest方法
1 public class Dispatcher { 2 3 public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException { 4 // 已经封装的则直接返回 5 if (request instanceof StrutsRequestWrapper) { 6 return request; 7 } else { 8 // 根据request的content-type不同封装成不同的装饰类 9 Object request; 10 if (this.isMultipartSupportEnabled(request) && this.isMultipartRequest(request)) { 11 MultiPartRequest multiPartRequest = this.getMultiPartRequest(); 12 LocaleProviderFactory localeProviderFactory = (LocaleProviderFactory)this.getContainer().getInstance(LocaleProviderFactory.class); 13 request = new MultiPartRequestWrapper(multiPartRequest, request, this.getSaveDir(), localeProviderFactory.createLocaleProvider(), this.disableRequestAttributeValueStackLookup); 14 } else { 15 // 封装成 StrutsRequestWrapper 类 16 request = new StrutsRequestWrapper(request, this.disableRequestAttributeValueStackLookup); 17 } 18 19 return (HttpServletRequest)request; 20 } 21 } 22 23 。。。。。。 24 25 }
(5)根据Http查找ActionMapping的findActionMapping方法
1 public class PrepareOperations { 2 3 public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response) { 4 return this.findActionMapping(request, response, false); 5 } 6 7 public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) { 8 ActionMapping mapping = (ActionMapping)request.getAttribute("struts.actionMapping"); 9 if (mapping == null || forceLookup) { 10 try { 11 // 使用 ActionMapper的实现类查找AcitonMapping 12 mapping = ((ActionMapper)this.dispatcher.getContainer().getInstance(ActionMapper.class)).getMapping(request, this.dispatcher.getConfigurationManager()); 13 if (mapping != null) { 14 request.setAttribute("struts.actionMapping", mapping); 15 } 16 } catch (Exception var6) { 17 if (this.dispatcher.isHandleException() || this.dispatcher.isDevMode()) { 18 this.dispatcher.sendError(request, response, 500, var6); 19 } 20 } 21 } 22 23 return mapping; 24 } 25 26 。。。。。。 27 }
其中要注意的是, ActionMapper负责处理URL映射关系的问题,有多个不同的实现类,
可以看出,这里又是Struts2使用设计模式中的策略模式的一个地方。
(6)执行逻辑的executeAction方法
1 public class ExecuteOperations { 2 private Dispatcher dispatcher; 3 4 public ExecuteOperations(Dispatcher dispatcher) { 5 this.dispatcher = dispatcher; 6 } 7 // 处理静态资源的方法 8 public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 9 String resourcePath = RequestUtils.getServletPath(request); 10 if ("".equals(resourcePath) && null != request.getPathInfo()) { 11 resourcePath = request.getPathInfo(); 12 } 13 14 StaticContentLoader staticResourceLoader = (StaticContentLoader)this.dispatcher.getContainer().getInstance(StaticContentLoader.class); 15 if (staticResourceLoader.canHandle(resourcePath)) { 16 staticResourceLoader.findStaticResource(resourcePath, request, response); 17 return true; 18 } else { 19 return false; 20 } 21 } 22 23 // 调用 Dispatcher 的方法, 执行核心逻辑的地方 24 public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { 25 this.dispatcher.serviceAction(request, response, mapping); 26 } 27 }
1 public class Dispatcher { 2 3 // Dispatcher的核心方法 4 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { 5 6 // 总体的数据环境 7 Map<String, Object> extraContext = this.createContextMap(request, response, mapping); 8 9 // 创建ValueStack, 若有,则做一个简单的复制, 若没有,从ActionContext获取当前线程的ValueStack 10 ValueStack stack = (ValueStack)request.getAttribute("struts.valueStack"); 11 boolean nullStack = stack == null; 12 if (nullStack) { 13 ActionContext ctx = ActionContext.getContext(); 14 if (ctx != null) { 15 stack = ctx.getValueStack(); 16 } 17 } 18 19 if (stack != null) { 20 // 将ValueStack设置到数据环境中 21 extraContext.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this.valueStackFactory.createValueStack(stack)); 22 } 23 24 String timerKey = "Handling request from Dispatcher"; 25 26 try { 27 UtilTimerStack.push(timerKey); 28 29 // 还记得这三个元素吗, 从ActionMapping中获取Action的三个要素 30 String namespace = mapping.getNamespace(); 31 String name = mapping.getName(); 32 String method = mapping.getMethod(); 33 34 // 获取ActionProxy,进入了XWork框架中 35 ActionProxy proxy = ((ActionProxyFactory)this.getContainer().getInstance(ActionProxyFactory.class)).createActionProxy(namespace, name, method, extraContext, true, false); 36 request.setAttribute("struts.valueStack", proxy.getInvocation().getStack()); 37 38 // 若ActionMapping中包含Result,则执行Result的方法,否则执行ActionProxy的调用 39 if (mapping.getResult() != null) { 40 Result result = mapping.getResult(); 41 result.execute(proxy.getInvocation()); 42 } else { 43 proxy.execute(); 44 } 45 46 if (!nullStack) { 47 request.setAttribute("struts.valueStack", stack); 48 } 49 } catch (ConfigurationException var17) { 50 this.logConfigurationException(request, var17); 51 this.sendError(request, response, 404, var17); 52 } catch (Exception var18) { 53 var18.printStackTrace(); 54 if (!this.handleException && !this.devMode) { 55 throw new ServletException(var18); 56 } 57 58 this.sendError(request, response, 500, var18); 59 } finally { 60 UtilTimerStack.pop(timerKey); 61 } 62 63 } 64 65 。。。。。。。 66 67 }
核心调度器Dispatcher负责对XWork框架中的元素进行调用。在进入XWork框架之后,Struts2的工作已经完成,接下来都交给XWork框架进行处理了。在这段代码中,可以看出,XWork中的ActionProxy,正是XWrok框架的入口,起到一个转接的作用。
接下来,我们将对XWork入口——ActionProxy进行分析。
在Dispatcher的serviceAction方法中,在获取ActionProxy的方法中,我们可以看到其中的四个参数——namespace, actionName, methodName和extraContext,而这些,正是构成ActionProxy数据源的参数。在这数据源中,前三个参数是在ActionMapper根据Http获取ActionMapping对象时获取的,而extraContext这个上下文对象,是通过Dispatcher中的createContextMap方法创建的。进入ActionInvocation中的createContextMap方法,查看ActionProxy如何初始化XWork的这个上下文环境的,
1 public class DefaultActionInvocation implements ActionInvocation { 2 protected Map<String, Object> extraContext; 3 protected Map<String, Object> createContextMap() { 4 Map contextMap; 5 // 已经包含ValueStack, 使用ValueStack的上下文 6 if (this.extraContext != null && this.extraContext.containsKey("com.opensymphony.xwork2.util.ValueStack.ValueStack")) { 7 this.stack = (ValueStack)this.extraContext.get("com.opensymphony.xwork2.util.ValueStack.ValueStack"); 8 if (this.stack == null) { 9 throw new IllegalStateException("There was a null Stack set into the extra params."); 10 } 11 12 contextMap = this.stack.getContext(); 13 } else { 14 // 没有ValueStack, 创建一个ValueStack 15 this.stack = this.valueStackFactory.createValueStack(); 16 contextMap = this.stack.getContext(); 17 } 18 // 将 extraContext中的内容放入上下文环境中 19 if (this.extraContext != null) { 20 contextMap.putAll(this.extraContext); 21 } 22 23 // 将 ActionInvocation 和 Container 放入 上下文环境中 24 contextMap.put("com.opensymphony.xwork2.ActionContext.actionInvocation", this); 25 contextMap.put("com.opensymphony.xwork2.ActionContext.container", this.container); 26 return contextMap; 27 } 28 29 。。。。。。 30 }
通过之前的文章,我们已经知道,ActionProxy只是提供Struts2和XWork之间联系的一个桥梁,正真负责对XWork框架元素的调用是ActionInvocation。 ActionInvocation的初始化过程在DefaultAcitionInvocation的init方法中,
1 public class DefaultActionInvocation implements ActionInvocation { 2 3 public void init(ActionProxy proxy) { 4 this.proxy = proxy; 5 // 创建上下文环境 6 Map<String, Object> contextMap = this.createContextMap(); 7 ActionContext actionContext = ActionContext.getContext(); 8 // 将ActionInvocation设置到ActionContext中,让ActionInvocation可以共享 9 if (actionContext != null) { 10 actionContext.setActionInvocation(this); 11 } 12 13 // 创建 Action 对象 14 this.createAction(contextMap); 15 16 // 这里是数据流与控制流相互关联的重要的一步, 将Action放入到ValueStack中 17 if (this.pushAction) { 18 this.stack.push(this.action); 19 contextMap.put("action", this.action); 20 } 21 // 将ActionInvocation的上下文和ActionContext等同 22 this.invocationContext = new ActionContext(contextMap); 23 this.invocationContext.setName(proxy.getActionName()); 24 // 获取拦截器堆栈 25 this.createInterceptors(proxy); 26 this.prepareLazyParamInjector(this.invocationContext.getValueStack()); 27 } 28 29 。。。。。。。。 30 }
查看DefaultActionInvocation的invoke方法,
1 public class DefaultActionInvocation implements ActionInvocation { 2 3 public String invoke() throws Exception { 4 String profileKey = "invoke: "; 5 6 String var21; 7 try { 8 UtilTimerStack.push(profileKey); 9 10 // 若已经执行过,直接抛异常 11 if (this.executed) { 12 throw new IllegalStateException("Action has already executed"); 13 } 14 15 // 对Interceptor集合的调用 16 if (this.interceptors.hasNext()) { 17 InterceptorMapping interceptorMapping = (InterceptorMapping)this.interceptors.next(); 18 String interceptorMsg = "interceptorMapping: " + interceptorMapping.getName(); 19 UtilTimerStack.push(interceptorMsg); 20 21 try { 22 Interceptor interceptor = interceptorMapping.getInterceptor(); 23 if (interceptor instanceof WithLazyParams) { 24 interceptor = this.lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), this.invocationContext); 25 } 26 27 // 这里 intercept方法实际上包含了一个递归调用 28 this.resultCode = interceptor.intercept(this); 29 } finally { 30 UtilTimerStack.pop(interceptorMsg); 31 } 32 } else { 33 // 递归中不再有 interceptor时,直接调用action 34 this.resultCode = this.invokeActionOnly(); 35 } 36 37 if (!this.executed) { 38 if (this.preResultListeners != null) { 39 LOG.trace("Executing PreResultListeners for result [{}]", this.result); 40 Iterator i$ = this.preResultListeners.iterator(); 41 // 对扩展点类的调用 42 while(i$.hasNext()) { 43 Object preResultListener = i$.next(); 44 PreResultListener listener = (PreResultListener)preResultListener; 45 String _profileKey = "preResultListener: "; 46 47 try { 48 UtilTimerStack.push(_profileKey); 49 listener.beforeResult(this, this.resultCode); 50 } finally { 51 UtilTimerStack.pop(_profileKey); 52 } 53 } 54 } 55 // 执行Result对象 56 if (this.proxy.getExecuteResult()) { 57 this.executeResult(); 58 } 59 60 this.executed = true; 61 } 62 63 var21 = this.resultCode; 64 } finally { 65 UtilTimerStack.pop(profileKey); 66 } 67 68 return var21; 69 } 70 71 。。。。。。。。。 72 }
这里说的其实就是在之前文章说提到过的,在XWork框架中,ActionInvocation对Interceptor,Action和Result整个执行栈的调用。查看调用Action的invokeActionOnly方法,
1 public class DefaultActionInvocation implements ActionInvocation { 2 3 public String invokeActionOnly() throws Exception { 4 return this.invokeAction(this.getAction(), this.proxy.getConfig()); 5 } 6 7 protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception { 8 String methodName = this.proxy.getMethod(); 9 LOG.debug("Executing action method = {}", methodName); 10 String timerKey = "invokeAction: " + this.proxy.getActionName(); 11 12 try { 13 Throwable t; 14 try { 15 UtilTimerStack.push(timerKey); 16 17 Object methodResult; 18 try { 19 methodResult = this.ognlUtil.callMethod(methodName + "()", this.getStack().getContext(), action); 20 } catch (MethodFailedException var16) { 21 if (!(var16.getReason() instanceof NoSuchMethodException)) { 22 throw var16; 23 } 24 25 if (!this.unknownHandlerManager.hasUnknownHandlers()) { 26 throw var16; 27 } 28 29 try { 30 methodResult = this.unknownHandlerManager.handleUnknownMethod(action, methodName); 31 } catch (NoSuchMethodException var15) { 32 throw var16; 33 } 34 35 if (methodResult == null) { 36 throw var16; 37 } 38 } 39 40 String var20 = this.saveResult(actionConfig, methodResult); 41 return var20; 42 } catch (NoSuchPropertyException var17) { 43 throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + this.getAction().getClass() + ""); 44 } catch (MethodFailedException var18) { 45 t = var18.getCause(); 46 if (this.actionEventListener != null) { 47 String result = this.actionEventListener.handleException(t, this.getStack()); 48 if (result != null) { 49 String var8 = result; 50 return var8; 51 } 52 } 53 } 54 55 if (t instanceof Exception) { 56 throw (Exception)t; 57 } else { 58 throw var18; 59 } 60 } finally { 61 UtilTimerStack.pop(timerKey); 62 } 63 } 64 65 。。。。。。。。。。。 66 }
只是对Action中定义的方法的调用以及逻辑判断。
至此,Struts2处理Http请求的流程已经结束。从StrutsPrepareAndExecuteFilter的doFilter方法出发,到PrepareOperation和ExecuteOperation类对Dispatcher类的方法的封装的调用,到从ActionProxy进入XWork框架后交由XWork框架全权进行逻辑处理,执行最后我们自己定义的Action的方法。整个流程下来,怎么说,荡气回肠。。。(〃'▽'〃)
Struts2源码的学习博客到此结束,写了四篇总结了对《Struts2技术内幕》这本书的一些理解吧,算是读后感。
写这篇文章的时候广行发面试短信了,周围的人都拿到offer了,我就零零星星这几个面试,心态BOOM,加油骚年 ̄へ ̄