1. 程式人生 > >Struts2核心工作原理解析

Struts2核心工作原理解析

這是Struts2官方站點提供的Struts 2 的整體結構。

一個請求在Struts2框架中的處理大概分為以下幾個步驟: 客戶端提起一個(HttpServletRequest)請求,如上文在瀏覽器中輸入”http://localhost:8080/TestMvc/add.action”就是提起一個(HttpServletRequest)請求。 請求被提交到一系列(主要是三層)的過濾器(Filter),如(ActionContextCleanUp、其他過濾器(SiteMesh等)、 FilterDispatcher)。注意這裡是有順序的,先ActionContextCleanUp,再其他過濾器(SiteMesh等)、最後到FilterDispatcher。 FilterDispatcher是控制器的核心,就是mvc中c控制層的核心。下面粗略的分析下我理解的FilterDispatcher工作流程和原理:FilterDispatcher進行初始化並啟用核心doFilter

其程式碼如下:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException …{ HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; ServletContext servletContext = filterConfig.getServletContext(); // 在這裡處理了HttpServletRequest和HttpServletResponse。 DispatcherUtils du = DispatcherUtils.getInstance(); du.prepare(request, response);//正如這個方法名字一樣進行locale、encoding以及特殊request parameters設定 try …{ request = du.wrapRequest(request, servletContext);//對request進行包裝 } catch (IOException e) …{ String message = “Could not wrap servlet request with MultipartRequestWrapper!”; LOG.error(message, e); throw new ServletException(message, e); } ActionMapperIF mapper = ActionMapperFactory.getMapper();//得到action的mapper ActionMapping mapping = mapper.getMapping(request);// 得到action 的 mapping if (mapping == null) …{ // 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(); } if (“true”.equals(Configuration.get(WebWorkConstants.WEBWORK_SERVE_STATIC_CONTENT)) && resourcePath.startsWith("/webwork")) …{ String name = resourcePath.substring("/webwork".length()); findStaticResource(name, response); } else …{ // this is a normal request, let it pass through chain.doFilter(request, response); } // WW did its job here return; } Object o = null; try …{ //setupContainer(request); o = beforeActionInvocation(request, servletContext); //整個框架最最核心的方法,下面分析 du.serviceAction(request, response, servletContext, mapping); } finally …{ afterActionInvocation(request, servletContext, o); ActionContext.setContext(null); } } du.serviceAction(request, response, servletContext, mapping); //這個方法詢問ActionMapper是否需要呼叫某個Action來處理這個(request)請求,如果ActionMapper決定需要呼叫某個Action,FilterDispatcher把請求的處理交給ActionProxy

public void serviceAction(HttpServletRequest request, HttpServletResponse response, String namespace, String actionName, Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap) …{ HashMap extraContext = createContextMap(requestMap, parameterMap, sessionMap, applicationMap, request, response, getServletConfig()); //例項化Map請求 ,詢問ActionMapper是否需要呼叫某個Action來處理這個(request)請求 extraContext.put(SERVLET_DISPATCHER, this); OgnlValueStack stack = (OgnlValueStack) request.getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY); if (stack != null) …{ extraContext.put(ActionContext.VALUE_STACK,new OgnlValueStack(stack)); } try …{ ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext); //這裡actionName是通過兩道getActionName解析出來的, FilterDispatcher把請求的處理交給ActionProxy,下面是ServletDispatcher的 TODO: request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack()); proxy.execute(); //通過代理模式執行ActionProxy if (stack != null)…{ request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY,stack); } } catch (ConfigurationException e) …{ log.error(“Could not find action”, e); sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) …{ log.error(“Could not execute action”, e); sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } }

FilterDispatcher詢問ActionMapper是否需要呼叫某個Action來處理這個(request)請求,如果ActionMapper決定需要呼叫某個Action,FilterDispatcher把請求的處理交給ActionProxy。 ActionProxy通過Configuration Manager(struts.xml)詢問框架的配置檔案,找到需要呼叫的Action類. 如上文的struts.xml配置

<?xml version="1.0" encoding="GBK"?> add.jsp

如果提交請求的是add.action,那麼找到的Action類就是edisundong.AddAction。 ActionProxy建立一個ActionInvocation的例項,同時ActionInvocation通過代理模式呼叫Action。但在呼叫之前ActionInvocation會根據配置載入Action相關的所有Interceptor。(Interceptor是struts2另一個核心級的概念)

下面我們來看看ActionInvocation是如何工作的:

ActionInvocation 是Xworks 中Action 排程的核心。而對Interceptor 的排程,也正是由ActionInvocation負責。ActionInvocation 是一個介面, 而DefaultActionInvocation 則是Webwork 對ActionInvocation的預設實現。

Interceptor 的排程流程大致如下:

  1. ActionInvocation初始化時,根據配置,載入Action相關的所有Interceptor。
  2. 通過ActionInvocation.invoke方法呼叫Action實現時,執行Interceptor。

Interceptor將很多功能從我們的Action中獨立出來,大量減少了我們Action的程式碼,獨立出來的行為具有很好的重用性。XWork、WebWork的許多功能都是有Interceptor實現,可以在配置檔案中組裝Action用到的Interceptor,它會按照你指定的順序,在Action執行前後執行。 那麼什麼是攔截器。 攔截器就是AOP(Aspect-Oriented Programming)的一種實現。(AOP是指用於在某個方法或欄位被訪問之前,進行攔截然後在之前或之後加入某些操作。) 攔截器的例子這裡就不展開了。 struts-default.xml檔案摘取的內容:

< interceptor name =“alias” class =“com.opensymphony.xwork2.interceptor.AliasInterceptor” /> < interceptor name =“autowiring” class =“com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor” /> < interceptor name =“chain” class =“com.opensymphony.xwork2.interceptor.ChainingInterceptor” /> < interceptor name =“conversionError” class =“org.apache.struts2.interceptor.StrutsConversionErrorInterceptor” /> < interceptor name =“createSession” class =“org.apache.struts2.interceptor.CreateSessionInterceptor” /> < interceptor name =“debugging” class =“org.apache.struts2.interceptor.debugging.DebuggingInterceptor” /> < interceptor name =“external-ref” class =“com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor” /> < interceptor name =“execAndWait” class =“org.apache.struts2.interceptor.ExecuteAndWaitInterceptor” /> < interceptor name =“exception” class =“com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor” /> < interceptor name =“fileUpload” class =“org.apache.struts2.interceptor.FileUploadInterceptor” /> < interceptor name =“i18n” class =“com.opensymphony.xwork2.interceptor.I18nInterceptor” /> < interceptor name =“logger” class =“com.opensymphony.xwork2.interceptor.LoggingInterceptor” /> < interceptor name =“model-driven” class =“com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor” /> < interceptor name =“scoped-model-driven” class =“com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor” /> < interceptor name =“params” class =“com.opensymphony.xwork2.interceptor.ParametersInterceptor” /> < interceptor name =“prepare” class =“com.opensymphony.xwork2.interceptor.PrepareInterceptor” /> < interceptor name =“static-params” class =“com.opensymphony.xwork2.interceptor.StaticParametersInterceptor” /> < interceptor name =“scope” class =“org.apache.struts2.interceptor.ScopeInterceptor” /> < interceptor name =“servlet-config” class =“org.apache.struts2.interceptor.ServletConfigInterceptor” /> < interceptor name =“sessionAutowiring” class =“org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor” /> < interceptor name =“timer” class =“com.opensymphony.xwork2.interceptor.TimerInterceptor” /> < interceptor name =“token” class =“org.apache.struts2.interceptor.TokenInterceptor” /> < interceptor name =“token-session” class =“org.apache.struts2.interceptor.TokenSessionStoreInterceptor” /> < interceptor name =“validation” class =“com.opensymphony.xwork2.validator.ValidationInterceptor” /> < interceptor name =“workflow” class =“com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor” /> < interceptor name =“store” class =“org.apache.struts2.interceptor.MessageStoreInterceptor” /> < interceptor name =“checkbox” class =“org.apache.struts2.interceptor.CheckboxInterceptor” /> < interceptor name =“profiling” class =“org.apache.struts2.interceptor.ProfilingActivationInterceptor” />

一旦Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果。如上文中將結構返回“add.jsp”,但大部分時候都是返回另外一個action,那麼流程又得走一遍………

一些預設攔截器的簡單說明;有興趣可以看下原始碼,原始碼就不貼了。 攔截器 名字 說明

Alias Interceptor alias 在不同請求之間將請求引數在不同名字件轉換,請求內容不變

Chaining Interceptor chain 讓前一個Action的屬性可以被後一個Action訪問,現在和chain型別的result()結合使用。

Checkbox Interceptor checkbox 添加了checkbox自動處理程式碼,將沒有選中的checkbox的內容設定為false,而html預設情況下不提交沒有選中的checkbox。

Cookies Interceptor cookies 使用配置的name,value來是指cookies

Conversion Error Interceptor conversionError 將錯誤從ActionContext中新增到Action的屬性欄位中。

Create Session Interceptor createSession 自動的建立HttpSession,用來為需要使用到HttpSession的攔截器服務。

Debugging Interceptor debugging 提供不同的除錯用的頁面來展現內部的資料狀況。

Execute and Wait Interceptor execAndWait 在後臺執行Action,同時將使用者帶到一箇中間的等待頁面。

Exception Interceptor exception 將異常定位到一個畫面

File Upload Interceptor fileUpload 提供檔案上傳功能

I18n Interceptor i18n 記錄使用者選擇的locale

Logger Interceptor logger 輸出Action的名字

Message Store Interceptor store 儲存或者訪問實現ValidationAware介面的Action類出現的訊息,錯誤,欄位錯誤等。

Model Driven Interceptor model-driven 如果一個類實現了ModelDriven,將getModel得到的結果放在Value Stack中。

Scoped Model Driven scoped-model-driven 如果一個Action實現了ScopedModelDriven,則這個攔截器會從相應的Scope中取出model呼叫Action的setModel方法將其放入Action內部。

Parameters Interceptor params 將請求中的引數設定到Action中去。

Prepare Interceptor prepare 如果Acton實現了Preparable,則該攔截器呼叫Action類的prepare方法。

Scope Interceptor scope 將Action狀態存入session和application的簡單方法。

Servlet Config Interceptor servletConfig 提供訪問HttpServletRequest和HttpServletResponse的方法,以Map的方式訪問。

Static Parameters Interceptor staticParams 從struts.xml檔案中將中的中的內容設定到對應的Action中。

Roles Interceptor roles 確定使用者是否具有JAAS指定的Role,否則不予執行。

Timer Interceptor timer 輸出Action執行的時間

Token Interceptor token 通過Token來避免雙擊

Token Session Interceptor tokenSession 和Token Interceptor一樣,不過雙擊的時候把請求的資料儲存在Session中

Validation Interceptor validation 使用action-validation.xml檔案中定義的內容校驗提交的資料。

Workflow Interceptor workflow 呼叫Action的validate方法,一旦有錯誤返回,重新定位到INPUT畫面

Parameter Filter Interceptor N/A 從引數列表中刪除不必要的引數

Profiling Interceptor profiling 通過引數啟用profi