1. 程式人生 > >Java程式設計師從笨鳥到菜鳥之(四十)細談struts2(四)struts2中action執行流程和原始碼分析

Java程式設計師從笨鳥到菜鳥之(四十)細談struts2(四)struts2中action執行流程和原始碼分析

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

首先我們看一下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)

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

            這裡寫圖片描述 你好! 這是你第一次使用 **Markdown編輯器** 所展示的歡迎頁。如果你想學習如何使用Markdown編輯器, 可以仔細閱讀這篇文章,瞭解一下Markdown的基本語法知識。

新的改變

我們對Markdown編輯器進行了一些功能拓展與語法支援,除了標準的Markdown編輯器功能,我們增加了如下幾點新功能,幫助你用它寫部落格:

  1. 全新的介面設計 ,將會帶來全新的寫作體驗;
  2. 在創作中心設定你喜愛的程式碼高亮樣式,Markdown 將程式碼片顯示選擇的高亮樣式 進行展示;
  3. 增加了 圖片拖拽 功能,你可以將本地的圖片直接拖拽到編輯區域直接展示;
  4. 全新的 KaTeX數學公式 語法;
  5. 增加了支援甘特圖的mermaid語法1 功能;
  6. 增加了 多螢幕編輯 Markdown文章功能;
  7. 增加了 焦點寫作模式、預覽模式、簡潔寫作模式、左右區域同步滾輪設定 等功能,功能按鈕位於編輯區域與預覽區域中間;
  8. 增加了 檢查列表 功能。

功能快捷鍵

撤銷:Ctrl/Command + Z 重做:Ctrl/Command + Y 加粗:Ctrl/Command + B 斜體:Ctrl/Command + I 標題:Ctrl/Command + Shift + H 無序列表:Ctrl/Command + Shift + U 有序列表:Ctrl/Command + Shift + O 檢查列表:Ctrl/Command + Shift + C 插入程式碼:Ctrl/Command + Shift + K 插入連結:Ctrl/Command + Shift + L 插入圖片:Ctrl/Command + Shift + G

合理的建立標題,有助於目錄的生成

直接輸入1次#,並按下space後,將生成1級標題。 輸入2次#,並按下space後,將生成2級標題。 以此類推,我們支援6級標題。有助於使用TOC語法後生成一個完美的目錄。

如何改變文字的樣式

強調文字 強調文字

加粗文字 加粗文字

標記文字

刪除文字

引用文字

H2O is是液體。

210 運算結果是 1024.

插入連結與圖片

連結: link.

圖片: Alt

帶尺寸的圖片: Alt

當然,我們為了讓使用者更加便捷,我們增加了圖片拖拽功能。

如何插入一段漂亮的程式碼片

部落格設定頁面,選擇一款你喜歡的程式碼片高亮樣式,下面展示同樣高亮的 程式碼片.

// An highlighted block var foo = 'bar'; 

生成一個適合你的列表

  • 專案
    • 專案
      • 專案
  1. 專案1
  2. 專案2
  3. 專案3
  • 計劃任務
  • 完成任務

建立一個表格

一個簡單的表格是這麼建立的:

專案 Value
電腦 $1600
手機 $12
導管 $1

設定內容居中、居左、居右

使用:---------:居中 使用:----------居左 使用----------:居右

第一列 第二列 第三列
第一列文字居中 第二列文字居右 第三列文字居左

SmartyPants

SmartyPants將ASCII標點字元轉換為“智慧”印刷標點HTML實體。例如:

TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash

建立一個自定義列表

Markdown
Text-to-HTML conversion tool
Authors
John
Luke

如何建立一個註腳

一個具有註腳的文字。2

註釋也是必不可少的

Markdown將文字轉換為 HTML

KaTeX數學公式

您可以使用渲染LaTeX數學表示式 KaTeX:

Gamma公式展示 Γ(n)=(n1)!nN\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N 是通過尤拉積分

Γ(z)=0tz1etdt&ThinSpace;. \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.

你可以找到更多關於的資訊 LaTeX 數學表示式here.

新的甘特圖功能,豐富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 現有任務
        已完成               :done,    des1, 2014-01-06,2014-01-08
        進行中               :active,  des2, 2014-01-09, 3d
        計劃一               :         des3, after des2, 5d
        計劃二               :         des4, after des3, 5d
  • 關於 甘特圖 語法,參考 這兒,

UML 圖表

可以使用UML圖表進行渲染。 Mermaid. 例如下面產生的一個序列圖::

張三李四王五你好!李四, 最近怎麼樣?你最近怎麼樣,王五?我很好,謝謝!我很好,謝謝!李四想了很長時間,文字太長了不適合放在一行.打量著王五...很好... 王五, 你怎麼樣?張三李四王五

這將產生一個流程圖。:

連結長方形圓角長方形菱形
  • 關於 Mermaid 語法,參考 這兒,

FLowchart流程圖

我們依舊會支援flowchart的流程圖:

  • 關於 Flowchart流程圖 語法,參考 這兒.

匯出與匯入

匯出

如果你想嘗試使用此編輯器, 你可以在此篇文章任意編輯。當你完成了一篇文章的寫作, 在上方工具欄找到 文章匯出 ,生成一個.md檔案或者.html檔案進行本地儲存。

匯入

如果你想載入一篇你寫過的.md檔案或者.html檔案,在上方工具欄可以選擇匯入功能進行對應副檔名的檔案匯入, 繼續你的創作。