Struts2的Action

Struts2的結構

Action是Strus2的核心邏輯之一。但這並不意味着Action是複雜的,實際上Action很簡單,一個普通的java類就能夠做爲一個Action。咱們之因此能夠很方便的使用Action,是由於Struts2的框架對Action作了最大的支持。就像是一個老大,手底下有一大羣牛逼的小弟,因此能夠成天無所事事。Action的邏輯簡單,可是圍繞着Action的各個組件的邏輯就複雜的不行了。上圖展現了struts2中一個從請求的獲取到響應的流程。java

一個請求被獲取後,會通過層層的Filter到達ActionMapper。ActionMapper能夠判斷該是否須要爲該請求調用一個Action。若是是的話,這個請求會被送給ActionProxy。ActionProxy會經過配置文件和請求生成一個ActionInvocation。ActionInvocation的主要任務是根據運行對應的Action方法,並獲取Result。在ActionInvocation中會先運行Interceptor,若是沒有從Interceptor中獲取Result,則會運行Action的方法。獲取result後,會檢查配置文件中Action對應的結果,並根據結果訪問頁面或者請求另一個Action(Action鏈)。安全


Action的實現

Action能夠經過三種方式實現:
session

1.一個類能夠是普通的java類,可是必須有一個execute()app

2.實現了Action接口,Action接口提供了execute()方法框架

3.繼承了ActionSupport類型,ActionSupport實現了Action和其餘的一些接口,提供了許多經常使用方法。工具


Action的流程

proxy應當是Action流程的開端,因此這裏從Proxy開始進行分析一直到獲取Result爲止。this

DefaultActionProxy的execute中首先會設置ActionContext,將當前請求的Context給予ActionContext。每一個ActionContext都是基於ThreadLocal且靜態的。也就說每一個請求都會生成一個ActionContext,這個ActionContext在這次請求是能夠全局調用的spa

接下來DefaultActionInvocation的invoke方法被調用了。在invoke方法中首先會查看攔截器,這些攔截器被存儲在名爲interceptors的列表中。遍歷這個鏈表,並運行攔截器的interceptor方法,將invocation自己做爲參數傳遞給interceptor方法。interceptor方法的最後會從新調用invocation的invoke方法,因此該方法是遞歸方法。攔截器會不斷遞歸interceptor方法,直到全部的攔截器都被調用過或者在一個攔截器中得到了一個resultCode。線程

invoke方法代碼:code

public String invoke() throws Exception {
        String profileKey = "invoke: ";
        try {
            ...
            if (interceptors.hasNext()) {
                final InterceptorMapping interceptor = 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;
                        ...
                        try {
                            UtilTimerStack.push(_profileKey);
                            listener.beforeResult(this, resultCode);
                        }
                        finally {
                            UtilTimerStack.pop(_profileKey);
                        }
                    }
                }
                if (proxy.getExecuteResult()) {
                    executeResult();
                }
                executed = true;
            }
            return resultCode;
        }
        finally {
            UtilTimerStack.pop(profileKey);
        }
    }

若是運行完全部攔截器都沒有獲取ResultCode,那麼就會運行請求指定的Action的方法了。該方法是經過反射機制獲取的。

經過反射機制獲取方法代碼:

protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
        String methodName = proxy.getMethod();
        ...
        try {
            UtilTimerStack.push(timerKey);

            boolean methodCalled = false;
            Object methodResult = null;
            Method method = null;
            try {
                method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
            } catch (NoSuchMethodException e) {
                ...
            }
            if (!methodCalled) {
                methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);
            }
            return saveResult(actionConfig, methodResult);
        } catch (NoSuchMethodException e) {
           ...
        } catch (InvocationTargetException e) {
           ...
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

獲取到一個ResultCode後會檢查PreResultListener,PreResultListener一般也是攔截器,可是不會運行interceptor方法,而是beforeResult方法。

最後會調用DefaultActionInvocation的executeResult方法。這個方法的做用是根據咱們獲取的resultcode,在配置中獲取一個Result對象,並同過Result對象的execute方法將響應結果發送出去。


Action與ServletAPI

Action的請求參數通常能夠經過「注入」的方式獲取,這種方式避免了Action與ServletAPI的耦合,可是也有不少狀況是須要直接訪問ServletAPI獲取的。Struts2也提供了許多訪問ServletAPI的方法。

ActionContext

ActionContext類提供了HttpServletRequest,HttpSession,ServletContext的獲取方法,它們分別對應JSP內置對象的request,session,application。可是ActionContext只能獲取request內容,也就是隻能從頁面獲取參數。

ActionContext的獲取方式爲 :

ActionContext actionContext = ActionContext.getContext();

ActionContext是action執行時的上下文,它存放着Action須要用到的各類對象.如:請求參數(Parameter),會話(Session),Servlet上下文(ServletContext),本地化(Locale)

每次執行Action的時候都會建立新的ActionContext,ActionContext是線程安全的,在同一個線程裏ActionContext是惟一的。

ActionContext對Servlet的一些內容進行了封裝,從而使Action與Serlvet解耦合。

ServletActionContext

ServletActionContext繼承於ActionContext,提供了一些直接訪問Serlvet的方法。SerlvetActionContext能夠操做Cookie.ServletActionContext能夠獲取的對象有:

HttpServletRequest——請求對象

HttpServletResponse——響應對象

ServletContext——上下文信息

ApplicationContext——Http頁面上下文

ActionContext與ServletActionContext都是基於WEB應用的,因此普通的java應用是沒法獲取ActionContext或ServletActionContext的。在運行的時候,它們的值將爲NULL。ActionContext和ServletActionContext均可以獲取WEB數據,可是ActionContext更偏向於值得操做而ServletActionContext更偏向Serlvet的操做。

Others

SessionAware,RequestAware,ApplicationAware,ParameterAware也能夠訪問頁面獲取的數據。要使用這些工具,Action就必須實現SessionAware,RequestAware,ApplicationAware,ParameterAware接口。並實現

Public void setSession(Map<String,Object> map);

Public void setRequest(Map<String,Object> map );

Public void setApplication(Map<String,Object> map);

Public void setParameter(Map<String,Object> map);

ServletRequestAware,ServletResponseAware能夠用來獲取HttpServletRequest和HttpServletResponse對象。要獲取這些對象,就必須實現ServletRequestAware,ServletResponseAware接口。並實現:

Public  void setServletRequest(HttpServletRequest request);

Public void  setServletResponse(HttpServletResponse response);


參考了許多資料,也看了點代碼,不過老實說仍是隻知其一;不知其二,望高手出來指點迷津:)

相關文章
相關標籤/搜索