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能夠經過三種方式實現:
session
1.一個類能夠是普通的java類,可是必須有一個execute()app
2.實現了Action接口,Action接口提供了execute()方法框架
3.繼承了ActionSupport類型,ActionSupport實現了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的請求參數通常能夠經過「注入」的方式獲取,這種方式避免了Action與ServletAPI的耦合,可是也有不少狀況是須要直接訪問ServletAPI獲取的。Struts2也提供了許多訪問ServletAPI的方法。
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繼承於ActionContext,提供了一些直接訪問Serlvet的方法。SerlvetActionContext能夠操做Cookie.ServletActionContext能夠獲取的對象有:
HttpServletRequest——請求對象
HttpServletResponse——響應對象
ServletContext——上下文信息
ApplicationContext——Http頁面上下文
ActionContext與ServletActionContext都是基於WEB應用的,因此普通的java應用是沒法獲取ActionContext或ServletActionContext的。在運行的時候,它們的值將爲NULL。ActionContext和ServletActionContext均可以獲取WEB數據,可是ActionContext更偏向於值得操做而ServletActionContext更偏向Serlvet的操做。
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);
參考了許多資料,也看了點代碼,不過老實說仍是隻知其一;不知其二,望高手出來指點迷津:)