本文來自:曹勝歡博客專欄。轉載請註明出處:http://blog.csdn.net/csh624366188java
首先咱們看一下struts官方給咱們提供的struts執行流程tomcat
從上面流程圖咱們能夠看出struts執行的流程大致分一下階段:session
1. 初始的請求經過一條標準的過濾器鏈,到達servlet 容器( 好比tomcat 容器,WebSphere 容器)。app
2. 過濾器鏈包括可選的ActionContextCleanUp 過濾器,用於系統整合技術,如SiteMesh 插件。框架
3. 接着調用FilterDispatcher,FilterDispatcher 查找ActionMapper,以肯定這個請求是否須要調用某個Action。學習
4. 若是ActionMapper 肯定須要調用某個Action,FilterDispatcher 將控制權交給ActionProxy。this
5. ActionProxy 依照框架的配置文件(struts.xml),找到須要調用的Action 類。spa
6. ActionProxy 建立一個ActionInvocation 的實例。ActionInvocation 先調用相關的攔截器(Action 調用以前的部分),最後調用Action。.net
7. 一旦Action 調用返回結果,ActionInvocation 根據struts.xml 配置文件,查找對應的轉發路徑。返回結果一般是(但不總是,也多是另外的一個Action 鏈)JSP 技術或者FreeMarker的模版技術的網頁呈現。Struts2 的標籤和其餘視圖層組件,幫助呈現咱們所須要的顯示結果。在此,我想說清楚一些,最終的顯示結果必定是HTML 標籤。標籤庫技術和其餘視圖層技術只是爲了動態生成HTML 標籤。插件
8. 接着按照相反次序執行攔截器鏈( 執行Action 調用以後的部分)。最後,響應經過濾器鏈返回(過濾器技術執行流程與攔截器同樣,都是先執行前面部分,後執行後面部)。若是過濾器鏈中存在ActionContextCleanUp,FilterDispatcher 不會清理線程局部的ActionContext。若是不存在ActionContextCleanUp 過濾器,FilterDispatcher 會清除全部線程局部變量。
下面咱們就來具體分析一下3-6四個步驟:
步驟三:FilterDispatcher 查找ActionMapper,以肯定這個請求是否須要調用某個Action。
1)
ActionMapping mapping; try { mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager()); } catch (Exception ex) { log.error("error getting ActionMapping", ex); dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); return; }
2)調用actionmapper去尋找對應的ActionMapping,由於actionmapper是一個接口,全部咱們去他對應的實現類(DefaultActionMapper)裏面去找getMapping方法,下面咱們來看一下實現類裏面的getMapping方法源代碼:
public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) { ActionMapping mapping = new ActionMapping(); String uri = 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); if (mapping.getName() == null) { return null; } parseActionName(mapping); return mapping; }
ActionMapping 表明struts.xml 文件中的一個Action 配置,被傳入到serviceAction 中。注意ActionMapping 不表明Action 集合,只表明某個對應的Action。若是是一個Action 請求,( 請求路徑在struts.xml 有對應的Action 配置,即actionmapping不爲空),則調用dispatcher.serviceAction() 處理。找到對應的ActionMapping,下一步就去找具體的執行哪個action,從FilterDispatcher源碼中咱們能夠找到下一步流程:
dispatcher.serviceAction(request, response, servletContext, mapping);
從上面能夠看出,FilterDispatcher類中是調用的serviceAction方法來尋找的去調用哪個action。serviceAction()方法做用:加載Action 類,調用Action 類的方法,轉向到響應結果。響應結果指<result/> 標籤所表明的對象。
步驟4、5、六:若是ActionMapper 肯定須要調用某個Action,FilterDispatcher 將控制權交給ActionProxy。
咱們來看一下具體的serviceAction源碼:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { Map<String, Object> extraContext = createContextMap (request, response, mapping, context); //1 如下代碼目的爲獲取ValueStack,代理在調用的時候使用的是本值棧的副本 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(); } } //2 建立ValueStack 的副本 if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); //3 這個是獲取配置文件中<action/> 配置的字符串,action 對象已經在覈心控制器中建立 String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); // xwork 的配置信息 Configuration config = configurationManager.getConfiguration(); //4 動態建立ActionProxy ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class). createActionProxy(namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); //5 調用代理 if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { proxy.execute(); } //6 處理結束後,恢復值棧的代理調用前狀態 if (!nullStack) { request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { //7 若是action 或者result 沒有找到,調用sendError 報404 錯誤 }
關於valuestack說明一下:
1.valueStack 的創建是在doFilter 的開始部分,在Action 處理以前。即便訪問靜態資源ValueStack 依然會創建,保存在request 做用域。
2. ValueStack 在邏輯上包含2 個部分:object stack 和context map,object stack 包含Action 與Action 相關的對象。
context map 包含各類映射關係。request,session,application,attr,parameters 都保存在context map 裏。
parameters: 請求參數
atrr: 依次搜索page, request, session, 最後application 做用域。
幾點說明:
1. Valuestack 對象保存在request 裏,對應的key 是ServletActionContext.STRUTS_VALUESTACK_KEY。調用代理以前首先建立Valuestack 副本,調用代理時使用副本,調用後使用原實例恢復。本處的值棧指object stack。
2. Dispatcher 實例,建立一個Action 代理對象。並把處理委託給代理對象的execute 方法。
最後咱們在一塊兒看一下ActionInvocation實現類中invoke方法執行的流程: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(); } 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); } } } if (proxy.getExecuteResult()) { executeResult(); } executed = true; } return resultCode; } finally { UtilTimerStack.pop(profileKey); } }
這裏算是執行action中方法的最後一步了吧,至此,action的整個流程就基本差很少了,從頭至尾看下來,說實話,感觸不少,不少不明白的地方,這算是近了本身最大的努力去看這些源碼,感受從裏面收穫了不少,裏面不少的機制和知識點值得咱們去學習,記住了聖思源張龍老師的那句話:源碼面前,一目瞭然