Struts2運行流程-源碼剖析
首先說一下查看這些框架源碼的感受,每一次深入探究 Spring、Struts 等框架源碼都有種深陷進去不能自拔的感覺,但是只要思路清晰,帶著心中各種疑問去一點一點深入,還是會帶給我很多欣喜,柳暗花明又一村的感覺,總的來說,這個工作並不是那麽枯燥,感覺還是蠻不錯的。
本文為原創,如需轉載,請標明出處:http://www.cnblogs.com/gudu1/p/7726172.html
下面代碼是 StrutsPrepareAndExcuteFilter.java 中的代碼
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throwsIOException, 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運行流程-源碼剖析