struts2處理請求的過程

來源:互聯網
上載者:User

官方的流程圖:


用戶端對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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.