官方的流程圖:
用戶端對tomcat伺服器發出請求,將請求封裝成HttpRequest對象,並進行預先處理操作(如設定編碼等);
通過web.xml檔案 找到struts2的前端控制器StrutsPrepareAndExcuteFilter,並調用doFilter()方法。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { prepare.setEncodingAndLocale(request, response); prepare.createActionContext(request, response); prepare.assignDispatcherToThread();if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {chain.doFilter(request, response);} else {request = prepare.wrapRequest(request);ActionMapping mapping = prepare.findActionMapping(request, response, true);if (mapping == null) {boolean handled = execute.executeStaticResourceRequest(request, response);if (!handled) {chain.doFilter(request, response);}} else {execute.executeAction(request, response, mapping);}} } finally { prepare.cleanupRequest(request); } }
doFilter中:
1、設定編碼;
2、建立ActionContext,建立ValueStack對象。
3、對請求進行重新封裝,根據請求內容的類型不同,返回不同的對象:
如果為multipart/form-data類型,則返回MultiPartRequestWrapper類型的對象,否則返回StrutsRequestWrapper類型的對象,MultiPartRequestWrapper是StrutsRequestWrapper的子類,而這兩個類都是HttpServletRequest介面的實現。
4、根據請求request擷取actionMapping對象
ActionMapping mapping = prepare.findActionMapping(request, response, true);
如果mapping為null,說明請求的不是Action,會調用execute.executeStaticResourceRequest(request, response);方法,請求靜態資源。
如果mapping不為null,調用execute.executeAction(request, response, mapping),在這個方法中又調用
dispatcher.serviceAction(request, response, servletContext, mapping);方法。
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException { Map<String, Object> extraContext = createContextMap(request, response, mapping, context); // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action 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(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration(); ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); // if the ActionMapping says to go straight to a result, do it! if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { proxy.execute(); } // If there was a previous value stack then set it back onto the request if (!nullStack) { request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { // WW-2874 Only log error if in devMode if(devMode) { String reqStr = request.getRequestURI(); if (request.getQueryString() != null) { reqStr = reqStr + "?" + request.getQueryString(); } LOG.error("Could not find action or result\n" + reqStr, e); } else { LOG.warn("Could not find action or result", e); } sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e); } catch (Exception e) { sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } finally { UtilTimerStack.pop(timerKey); } }
在這個serviceAction方法中,
1、將相關對象資訊封裝為Map(如:HttpServletRequest、Http parameters、HttpServletResponse、HttpSession、ServletContext、ActionMapping等對象資訊),存入到執行內容Map中,返回執行內容Map對象extraMap;
2、擷取ValueStack對象,並放入map中
3、擷取mapping對象中儲存的action命名空間、name屬性、method屬性等資訊;
4、載入並解析Struts2設定檔,如果沒有人為配置,預設按順序載入struts-default.xml、struts-plugin.xml、struts.xml,將action配置、result配置、interceptor配置,解析並存入至config對象中,返迴文件設定物件config;
5、 根據執行內容Map、action命名空間、name屬性、method屬性等建立ActionProxy對象;
createActionProxy()方法中:
5.1、建立invacation:ActionInvocation inv= new DefaultActionInvocation(extraContext, true);
5.2、建立proxy:DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
5.3、接著,proxy.prepare();方法中
5.4、初始化action:invocation.init(this);方法中
使用反射建立action,並壓入值棧棧頂,準備攔截器集合。
接著:
6、 執行ActionProxy對象的proxy.execute()方法,並轉向結果;
public String execute() throws Exception { ActionContext previous = ActionContext.getContext(); ActionContext.setContext(invocation.getInvocationContext()); try {// This is for the new API:// return RequestContextImpl.callInContext(invocation, new Callable<String>() {// public String call() throws Exception {// return invocation.invoke();// }// }); return invocation.invoke(); } finally { if (cleanupContext) ActionContext.setContext(previous); } }
該方法調用了invocation.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(); } // this is needed because the result will be executed, then control will return to the Interceptor, which will // return above and flow through again 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); } } } // now execute the result, if we're supposed to if (proxy.getExecuteResult()) { executeResult(); } executed = true; } return resultCode; } finally { UtilTimerStack.pop(profileKey); } }
該方法實現了對攔截器的遞迴調用,攔截器的實現採用了責任鏈模式,所有攔截器必須實現介面Interceptor的intercept(ActionInvocation invocation)方法,該方法的參數為ActionInvocation,所以在方法最後調用invocation.invoke()方法就實現了攔截器的遞迴調用。
攔截器調用一遍,直到最後一個invoke()方法,攔截器列表中沒有未執行的攔截器,這時,會執行action相應的方法,並得到resultCode,接著,在返回Result之前,會執行PreResultListener的beforeResult()方法 ,最後執行結果,找到resultCode對應的結果類型,產生result對象,根據result的資訊,或者產生相應response,或者根據結果類型繼續執行。最後,對於本次Action請求將相應的執行狀態標誌位設定,返回resultCode。
這時最後一個攔截器中的invoke()方法得到傳回值,由於action和result相應的執行狀態標誌已經設定為執行過,所以通過上級攔截器中的invoke()方法將對下級攔截器的intercept()方法後的代碼進行判斷,action、result只執行一遍。然後攔截器由後往前返回,到此整個請求處理流程結束。
回顧整個流程:
a) 用戶端初始化一個指向Servlet容器的請求;
b) 根據Web.xml配置,請求首先經過核心過濾器StrutsPrepareAndExcuteFilter,執行doFilter方法,在該方法中,這隻編碼,建立valuestack對象,詢問ActionMapper來決定這個請求是否需要調用某個Action;如果ActionMapper決定需要調用某個Action,則ActionMapper會返回一個ActionMapping執行個體(儲存Action的配置資訊),調用executeAction()方法,
c)調用dispatcher.serviceAction()方法,建立ActionProxy(Action代理)對象,將請求交給代理對象繼續處理;
d) ActionProxy對象根據ActionMapping和Configuration Manager詢問架構的設定檔,找到需要調用的Action類;
e) ActionProxy對象建立時,會同時建立一個ActionInvocation的執行個體,並對action進行初始化,壓入值棧棧頂;
f) 執行proxy.execute()方法,調用invocation.invoke()方法
f) ActionInvocation的invoke()方法中,在調用Action的過程前後,涉及到相關攔截器(Intercepter)的調用;
g) 一旦Action執行完畢,ActionInvocation執行個體負責根據struts.xml中的配置建立並返回Result。Result通常是一個需要被表示的JSP或者FreeMarker的模版,也可能是另外的一個Action鏈;
h) 如果要在返回Result之前做些什麼,可以實現PreResultListener介面,PreResultListener可以在Interceptor中實現,也可以在Action中實現;
i) 根據Result對象資訊,產生使用者響應資訊response,在產生響應過程中可以使用Struts2 架構中繼承的標籤,在此過程中仍會再次涉及到ActionMapper;
本文參考http://mktao.blog.51cto.com/5866429/978913