1. 程式人生 > >基於Struts2.5.16的呼叫過程

基於Struts2.5.16的呼叫過程

引用一張Struts2呼叫的時序圖:

1.使用者傳送請求;這個請求被交給你在web.xml檔案配置的過濾器(Filter)struts2的過濾器(StrutsPrepareAndExecuteFilter)一般都是在最後一個。呼叫doFilter方法。

    <filter-mapping>  
        <filter-name>userLoginFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping> 

	<!-- 配置struts2的核心攔截器,官方推出用StrutsPreparedAndExcuteFilter代替FilterDispatcher -->
	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>*.action</url-pattern>
	</filter-mapping>

StrutsPrepareAndExecuteFilter的doFiler方法:

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            String uri = RequestUtils.getUri(request);
            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
                chain.doFilter(request, response);
            } else {
                LOG.trace("Checking if {} is a static resource", uri);
                boolean handled = execute.executeStaticResourceRequest(request, response);
                if (!handled) {
                    LOG.trace("Assuming uri {} as a normal action", uri);
                    prepare.setEncodingAndLocale(request, response);
                    prepare.createActionContext(request, response);
                    prepare.assignDispatcherToThread();
                    request = prepare.wrapRequest(request);
                    ActionMapping mapping = prepare.findActionMapping(request, response, true);
                    if (mapping == null) {
                        LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
                        chain.doFilter(request, response);
                    } else {
                        LOG.trace("Found mapping {} for {}", mapping, uri);
                        execute.executeAction(request, response, mapping);
                    }
                }
            }
        } finally {
            prepare.cleanupRequest(request);
        }
    }

2. 呼叫PrepareOperations的findActionMapping(request, response, true),以便確定這個請求是否有對應的action呼叫。 返回一個描述action呼叫的ActionMapping物件 。具體實現類是org.apache.struts2.dispatcher.mapper.DefaultActionMapper的getMapping方法。

    public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
        ActionMapping mapping = new ActionMapping();
        String uri = RequestUtils.getUri(request);

        int indexOfSemicolon = uri.indexOf(';');
        uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;

        uri = dropExtension(uri, mapping);
        if (uri == null) {
            return null;
        }

        parseNameAndNamespace(uri, mapping, configManager);
        handleSpecialParameters(request, mapping);
        return parseActionName(mapping);
    }

3. ExecuteOperations呼叫executeAction處理Action。其實質是呼叫Dispatcher類的serviceAction方法得到一個ActionProxy例項。該方法根據ActionMapping這個物件中的屬性以及configurationManager來生成一個ActionProxy。具體實現類是DefaultActionProxyFactory。

            ActionProxy proxy = 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();
            }

4. ActionProxy設定ActionInvocation物件的執行上下文,然後呼叫其invoke方法。從攔截器對映中查詢尚未執行的攔截器,呼叫他的intercept(invocation)方法,並將自身物件引用作為引數傳遞給攔截器。 

5. 攔截器完成某些預處理工作後,反過來呼叫ActionInvocation的invoke方法,ActionInvocation維護者自己的狀態,所以他知道哪些攔截器已經被執行,如果還沒有執行的攔截器,就繼續執行他的intercept(invocation)方法。 至所有的攔截器都已經執行過了,就呼叫action例項的execute方法 。

6. ActionInvocation根據action執行返回的結果碼,查詢對應的Result,呼叫Result的execute(invocation)方法,將結果頁面呈現給使用者。 ActionInvocation的invoke方法將控制權返回給攔截器對映中的最後一個攔截器,該攔截器完成所有必須的後期處理工作,然後從intercept(invocation)方法返回,允許前一個攔截器執行他自己的後處理工作。如此迴圈到所有的攔截器都成功返回。

7. ActionInvocation的invoke方法執行完畢後,想ActionProxy返回一個String型別的結果碼,最後,ActionProxy清理狀態並返回。 

ps:關於ActionContext是何時建立的,可以參考下文:

https://blog.csdn.net/u011496144/article/details/74931136