Struts2运行流程-源码剖析
Posted Mr丶苏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2运行流程-源码剖析相关的知识,希望对你有一定的参考价值。
首先说一下查看这些框架源码的感受,每一次深入探究 Spring、Struts 等框架源码都有种深陷进去不能自拔的感觉,但是只要思路清晰,带着心中各种疑问去一点一点深入,还是会带给我很多欣喜,柳暗花明又一村的感觉,总的来说,这个工作并不是那么枯燥,感觉还是蛮不错的。
本文为原创,如需转载,请标明出处:http://www.cnblogs.com/gudu1/p/7726172.html
下面代码是 StrutsPrepareAndExcuteFilter.java 中的代码
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { prepare.setEncodingAndLocale(request, response); ① prepare.createActionContext(request, response); ② prepare.assignDispatcherToThread(); ③ 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); ⑧ } }
相信用过 Struts2 的朋友都知道在项目搭建的过程中要在 web.xml 中配置 这个 Filter 类,它跟SpringMVC中的 DispatcherServlet 类的共同点是拦截请求然后进行一系列处理,那么Struts2的处理就是上面这个方法,看起来很简单对吗?事实上就是很简单,那么继续看:
首先请求进入Tomcat 容器,由Tomcat创建 ServletRequest 和 ServletResponse 以及 FilterChain 对象并传入 doFilter(req,res,chain)。首先把 req 和 res 进行向下转型,接着判断web容器中是否还有过滤器,如果有就执行下一个过滤器,没有就进行请求资源:
· ①:设置字符集编码和环境
②:创建 ActionContext 对象,使用的是 PrepareOperations.java 中的方法。
③:创建一个 Dispatcher 对象保存在当前线程中,如果该线程中存在直接返回,如果没有就创建一个并保存。
④:包装 HttPServletRequest 对象,如果是该请求是上传文件包装成Struts2中的 MultiPartRequestWrapper 对象,否者是 StrutsRequestWrapper 对象,然后把包装对象保存在 ActionContext 的 Map 集合中。
⑤:通过 URI 截取判断是请求静态资源还是请求Action,如果是请求静态资源直接返回 NULL,否则就在 request 中寻找 Action ,在Struts2中 request 对象是保存在值栈中的,如果存在就直接返回,没有就创建一个保存在request中并返回。
⑥ - ⑦:如果是请求静态资源,就执行 executeStaticResourceRequest() 方法,否则就执行 executeAction() 方法。
⑧:请求已经结束了,因为Struts2是多例,最后要销毁资源,比如 ActionContext,Dispatcher。
上面 从第 ④ 步到最后的源码都会贴出来,因为代码众多,所以这里只贴出部分帮助理解:
代码先就放在这里,日后有时间再写出解释。
④:
org.apache.struts2.dispatcher.ng.PrepareOperations
public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException { HttpServletRequest request = oldRequest; try { // Wrap request first, just in case it is multipart/form-data // parameters might not be accessible through before encoding (ww-1278) request = dispatcher.wrapRequest(request); ServletActionContext.setRequest(request); } catch (IOException e) { throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e); } return request; }
org.apache.struts2.dispatcher.Dispatcher.wrapRequest(HttpServletRequest)
public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException { // don\'t wrap more than once if (request instanceof StrutsRequestWrapper) { return request; } String content_type = request.getContentType(); if (content_type != null && content_type.contains("multipart/form-data")) { MultiPartRequest mpr = getMultiPartRequest(); LocaleProvider provider = getContainer().getInstance(LocaleProvider.class); request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup); } else { request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup); } return request; }
⑤:
org.apache.struts2.dispatcher.ng.PrepareOperations.findActionMapping(HttpServletRequest, HttpServletResponse, boolean)
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) { ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY); if (mapping == null || forceLookup) { try { mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager()); if (mapping != null) { request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping); } } catch (Exception ex) { if (dispatcher.isHandleException() || dispatcher.isDevMode()) { dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); } } } return mapping; }
org.apache.struts2.dispatcher.mapper.DefaultActionMapper.getMapping(HttpServletRequest, ConfigurationManager)
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) { ActionMapping mapping = new ActionMapping(); String uri = RequestUtils.getUri(request); int indexOfSemicolon = uri.indexOf(";"); uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri; uri = dropExtension(uri, mapping); if (uri == null) { return null; } parseNameAndNamespace(uri, mapping, configManager); handleSpecialParameters(request, mapping); return parseActionName(mapping); }
⑥:
org.apache.struts2.dispatcher.ng.ExecuteOperations.executeStaticResourceRequest(HttpServletRequest, HttpServletResponse)
public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // there is no action in this request, should we look for a static resource? String resourcePath = RequestUtils.getServletPath(request); if ("".equals(resourcePath) && null != request.getPathInfo()) { resourcePath = request.getPathInfo(); } StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class); if (staticResourceLoader.canHandle(resourcePath)) { staticResourceLoader.findStaticResource(resourcePath, request, response); // The framework did its job here return true; } else { // this is a normal request, let it pass through return false; } }
org.apache.struts2.dispatcher.DefaultStaticContentLoader.findStaticResource(String, HttpServletRequest, HttpServletResponse)
public void findStaticResource(String path, HttpServletRequest request, HttpServletResponse response) throws IOException { String name = cleanupPath(path); for (String pathPrefix : pathPrefixes) { URL resourceUrl = findResource(buildPath(name, pathPrefix)); if (resourceUrl != null) { InputStream is = null; try { //check that the resource path is under the pathPrefix path String pathEnding = buildPath(name, pathPrefix); if (resourceUrl.getFile().endsWith(pathEnding)) is = resourceUrl.openStream(); } catch (IOException ex) { // just ignore it continue; } //not inside the try block, as this could throw IOExceptions also if (is != null) { process(is, path, request, response); return; } } } response.sendError(HttpServletResponse.SC_NOT_FOUND); }
⑦:
org.apache.struts2.dispatcher.Dispatcher.serviceAction(HttpServletRequest, HttpServletResponse, ActionMapping)
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { Map<String, Object> extraContext = createContextMap(request, response, mapping); // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); boolean nullStack = stack == null; if (nullStack) { ActionContext ctx = ActionContext.getContext(); if (ctx != null) { stack = ctx.getValueStack(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); ActionProxy proxy = 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(); } // If there was a previous value stack then set it back onto the request if (!nullStack) { request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { logConfigurationException(request, e); sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { if (handleException || devMode) { sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } else { throw new ServletException(e); } } finally { UtilTimerStack.pop(timerKey); } }
com.opensymphony.xwork2.DefaultActionProxy.execute()
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; }
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 = 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) { LOG.trace("Executing PreResultListeners for result [#0]", result); 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); } }
⑧:
org.apache.struts2.dispatcher.ng.PrepareOperations.cleanupRequest(HttpServletRequest)
public void cleanupRequest(HttpServletRequest request) { Integer counterVal = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER); if (counterVal != null) { counterVal -= 1; request.setAttribute(CLEANUP_RECURSION_COUNTER, counterVal); if (counterVal > 0 ) { if (log.isDebugEnabled()) { log.debug("skipping cleanup counter="+counterVal); } return; } } // always clean up the thread request, even if an action hasn\'t been executed try { dispatcher.cleanUpRequest(request); } finally { ActionContext.setContext(null); Dispatcher.setInstance(null); } }
以上是关于Struts2运行流程-源码剖析的主要内容,如果未能解决你的问题,请参考以下文章
[原创]java WEB学习笔记70:Struts2 学习之路-- struts2拦截器源码分析,运行流程
深度挖掘 RocketMQ底层源码「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(Pull模式-下)
2018.11.20 Struts2中对结果处理方式分析&struts2内置的方式底层源码剖析
Struts2剖析Struts2中的反射技术 ValueStack(值栈)
精华推荐 | 深入浅出 RocketMQ原理及实战「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行核心的流程(上篇)
深度挖掘RocketMQ底层源码「底层源码挖掘系列」透彻剖析贯穿RocketMQ的消费者端的运行调度的流程(Pull模式)