Java程式員從笨鳥到菜鳥之(四十)細談struts2(四)struts2中action執行流程和源碼分析

來源:互聯網
上載者:User

本文來自:曹勝歡部落格專欄。轉載請註明出處: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 調用之後的部分)。最後,響應通過濾器鏈返回(過濾器技術執行流程與攔截器一樣,都是先執行前面部分,後執行後面部)。如果過濾器鏈中存在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/> 標籤所代表的對象。

步驟四、五、六:如果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 動態建立ActionProxyActionProxy 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的整個流程就基本差不多了,從頭到尾看下來,說實話,感觸很多,很多不明白的地方,這算是近了自己最大的努力去看這些源碼,感覺從裡面收穫了很多,裡面很多的機制和知識點值得我們去學習,記住了聖思源張龍老師的那句話:源碼面前,一目瞭然

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.