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"?> 
 <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">
 <struts> 
     <include file="struts-default.xml"/> 
     <package name="struts2" extends="struts-default"> 
         <action name="add"  
             class="edisundong.AddAction" > 
             <result>add.jsp</result> 
         </action>     
     </package> 
 </struts>

如果提交請求的是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(<result type=”chain”>)結合使用。 

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>中的<param>中的內容設置到對應的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

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