struts2中action執行流程和源碼分析

本文來自:曹勝歡博客專欄。轉載請註明出處:http://blog.csdn.net/csh624366188

 

首先我們看一下struts官方給我們提供的struts執行流程

從上面流程圖我們可以看出struts執行的流程大體分一下階段:

1. 初始的請求通過一條標準的過濾器鏈,到達servlet 容器比如tomcat 容器,WebSphere 容器)

2. 過濾器鏈包括可選的ActionContextCleanUp 過濾器,用於系統整合技術,如SiteMesh 插件。

3. 接着調用FilterDispatcher,FilterDispatcher 查找ActionMapper,以確定這個請求是否需要調用某個Action。

4. 如果ActionMapper 確定需要調用某個Action,FilterDispatcher 將控制權交給ActionProxy。

5. ActionProxy 依照框架的配置文件(struts.xml),找到需要調用的Action 類。

6. ActionProxy 創建一個ActionInvocation 的實例。ActionInvocation 先調用相關的攔截器(Action 調用之前的部分),最後調用Action。

7. 一旦Action 調用返回結果,ActionInvocation 根據struts.xml 配置文件,查找對應的轉發路徑。返回結果通常是(但不總是,也可能是另外的一個Action 鏈)JSP 技術或者FreeMarker的模版技術的網頁呈現。Struts2 的標籤和其他視圖層組件,幫助呈現我們所需要的顯示結果。在此,我想說清楚一些,最終的顯示結果一定是HTML 標籤。標籤庫技術和其他視圖層技術只是爲了動態生成HTML 標籤。

8. 接着按照相反次序執行攔截器鏈執行Action 調用之後的部分)。最後,響應通過濾器鏈返回(過濾器技術執行流程與攔截器一樣,都是先執行前面部分,後執行後面部)。如果過濾器鏈中存在ActionContextCleanUpFilterDispatcher 不會清理線程局部的ActionContext。如果不存在ActionContextCleanUp 過濾器,FilterDispatcher 會清除所有線程局部變量。

下面我們就來具體分析一下3-6四個步驟:

步驟三:FilterDispatcher 查找ActionMapper,以確定這個請求是否需要調用某個Action。

1)

  1. ActionMapping mapping;  
  2.   
  3.             try {  
  4.   
  5.                 mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());  
  6.   
  7.             } catch (Exception ex) {  
  8.   
  9.                 log.error("error getting ActionMapping", ex);  
  10.   
  11.                 dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);  
  12.   
  13.                 return;  
  14.             }  

  

2調用actionmapper去尋找對應的ActionMapping因爲actionmapper是一個接口,所有我們去他對應的實現類(DefaultActionMapper裏面去找getMapping方法,下面我們來看一下實現類裏面的getMapping方法源代碼:

 

  1. public ActionMapping getMapping(HttpServletRequest request,  
  2.   
  3.                                     ConfigurationManager configManager) {  
  4.   
  5.         ActionMapping mapping = new ActionMapping();  
  6.   
  7.         String uri = getUri(request);  
  8.         int indexOfSemicolon = uri.indexOf(";");  
  9.   
  10.         uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;  
  11.   
  12.         uri = dropExtension(uri, mapping);  
  13.   
  14.         if (uri == null) {  
  15.   
  16.             return null;  
  17.   
  18.         }  
  19.   
  20.         parseNameAndNamespace(uri, mapping, configManager);  
  21.   
  22.         handleSpecialParameters(request, mapping);  
  23.   
  24.         if (mapping.getName() == null) {  
  25.   
  26.             return null;  
  27.   
  28.         }  
  29.         parseActionName(mapping);  
  30.   
  31.         return mapping;  
  32. }  


      ActionMapping 代表struts.xml 文件中的一個Action 配置,被傳入到serviceAction 中。注意ActionMapping 不代表Action 集合,只代表某個對應的Action。如果是一個Action 請求,( 請求路徑在struts.xml 有對應的Action 配置,即actionmapping不爲空),則調用dispatcher.serviceAction() 處理。找到對應的ActionMapping,下一步就去找具體的執行哪一個action,從FilterDispatcher源碼中我們可以找到下一步流程:

  1. dispatcher.serviceAction(request, response, servletContext, mapping);  


    從上面可以看出,FilterDispatcher類中是調用的serviceAction方法來尋找的去調用哪一個action。serviceAction()方法作用:加載Action 類,調用Action 類的方法,轉向到響應結果。響應結果指<result/> 標籤所代表的對象。

步驟四、五、六:如果ActionMapper 確定需要調用某個Action,FilterDispatcher 將控制權交給ActionProxy。

我們來看一下具體的serviceAction源碼:

  1. public void serviceAction(HttpServletRequest request, HttpServletResponse response,  
  2.   
  3. ServletContext context, ActionMapping mapping) throws ServletException {  
  4.   
  5. Map<String, Object> extraContext = createContextMap  
  6.   
  7. (request, response, mapping, context);  
  8.   
  9. //1 以下代碼目的爲獲取ValueStack,代理在調用的時候使用的是本值棧的副本  
  10.   
  11. ValueStack stack = (ValueStack) request.getAttribute  
  12.   
  13. (ServletActionContext.STRUTS_VALUESTACK_KEY);  
  14.   
  15. boolean nullStack = stack == null;  
  16.   
  17. if (nullStack) {  
  18.   
  19. ActionContext ctx = ActionContext.getContext();  
  20.   
  21. if (ctx != null) {  
  22.   
  23. stack = ctx.getValueStack();  
  24.   
  25. }  
  26.   
  27. }  
  28.   
  29. //2 創建ValueStack 的副本  
  30.   
  31. if (stack != null) {  
  32.   
  33. extraContext.put(ActionContext.VALUE_STACK,  
  34.   
  35. valueStackFactory.createValueStack(stack));  
  36.   
  37. }  
  38.   
  39. String timerKey = "Handling request from Dispatcher";  
  40.   
  41. try {  
  42.   
  43. UtilTimerStack.push(timerKey);  
  44.   
  45. //3 這個是獲取配置文件中<action/> 配置的字符串,action 對象已經在覈心控制器中創建  
  46.   
  47. String namespace = mapping.getNamespace();  
  48.   
  49. String name = mapping.getName();  
  50.   
  51. String method = mapping.getMethod();  
  52.   
  53. // xwork 的配置信息  
  54.   
  55. Configuration config = configurationManager.getConfiguration();  
  56.   
  57. //4 動態創建ActionProxy  
  58.   
  59. ActionProxy proxy =  
  60.   
  61. config.getContainer().getInstance(ActionProxyFactory.class).  
  62.   
  63. createActionProxy(namespace, name, method, extraContext, truefalse);  
  64.   
  65. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY,  
  66.   
  67. proxy.getInvocation().getStack());  
  68.   
  69. //5 調用代理  
  70.   
  71. if (mapping.getResult() != null) {  
  72.   
  73. Result result = mapping.getResult();  
  74.   
  75. result.execute(proxy.getInvocation());  
  76.   
  77. else {  
  78.   
  79. proxy.execute();  
  80.   
  81. }  
  82.   
  83. //6 處理結束後,恢復值棧的代理調用前狀態  
  84.   
  85. if (!nullStack) {  
  86.   
  87. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);  
  88.   
  89. }  
  90.   
  91. catch (ConfigurationException e) {  
  92.   
  93. //7 如果action 或者result 沒有找到,調用sendError 報404 錯誤  
  94.   
  95. }  


關於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源代碼:

  1.  public String invoke() throws Exception {  
  2.   
  3.         String profileKey = "invoke: ";  
  4.   
  5.         try {  
  6.   
  7.             UtilTimerStack.push(profileKey);  
  8.   
  9.             if (executed) {  
  10.   
  11.                 throw new IllegalStateException("Action has already executed");  
  12.   
  13.             }  
  14.   
  15.             if (interceptors.hasNext()) {  
  16.   
  17.                 final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();  
  18.   
  19.                 String interceptorMsg = "interceptor: " + interceptor.getName();  
  20.   
  21.                 UtilTimerStack.push(interceptorMsg);  
  22.   
  23.                 try {  
  24.   
  25.                                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);  
  26.   
  27.                             }  
  28.   
  29.                 finally {  
  30.   
  31.                     UtilTimerStack.pop(interceptorMsg);  
  32.   
  33.                 }  
  34.   
  35.             } else {  
  36.   
  37.                 resultCode = invokeActionOnly();  
  38.   
  39.             }  
  40.   
  41.   
  42.                if (!executed) {  
  43.   
  44.                 if (preResultListeners != null) {  
  45.   
  46.                     for (Object preResultListener : preResultListeners) {  
  47.   
  48.                         PreResultListener listener = (PreResultListener) preResultListener;  
  49.   
  50.   
  51.                         String _profileKey = "preResultListener: ";  
  52.   
  53.                         try {  
  54.   
  55.                             UtilTimerStack.push(_profileKey);  
  56.   
  57.                             listener.beforeResult(this, resultCode);  
  58.   
  59.                         }  
  60.   
  61.                         finally {  
  62.   
  63.                             UtilTimerStack.pop(_profileKey);  
  64.   
  65.                         }  
  66.   
  67.                     }  
  68.   
  69.                 }  
  70.   
  71.   
  72.                    if (proxy.getExecuteResult()) {  
  73.   
  74.                     executeResult();  
  75.   
  76.                 }  
  77.   
  78.   
  79.                 executed = true;  
  80.   
  81.             }  
  82.             return resultCode;  
  83.   
  84.         }  
  85.   
  86.         finally {  
  87.   
  88.             UtilTimerStack.pop(profileKey);  
  89.   
  90.        }  
  91. }  


        這裏算是執行action中方法的最後一步了吧,至此,action的整個流程就基本差不多了,從頭到尾看下來,說實話,感觸很多,很多不明白的地方,這算是近了自己最大的努力去看這些源碼,感覺從裏面收穫了很多,裏面很多的機制和知識點值得我們去學習,記住了聖思源張龍老師的那句話:源碼面前,一目瞭然

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章