1. 程式人生 > >Struts2運行流程-源碼剖析

Struts2運行流程-源碼剖析

rac n) wrap sem .com xtra substring 字符集 原創

  首先說一下查看這些框架源碼的感受,每一次深入探究 Spring、Struts 等框架源碼都有種深陷進去不能自拔的感覺,但是只要思路清晰,帶著心中各種疑問去一點一點深入,還是會帶給我很多欣喜,柳暗花明又一村的感覺,總的來說,這個工作並不是那麽枯燥,感覺還是蠻不錯的。

本文為原創,如需轉載,請標明出處:http://www.cnblogs.com/gudu1/p/7726172.html

下面代碼是 StrutsPrepareAndExcuteFilter.java 中的代碼

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws
IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); }
else { prepare.setEncodingAndLocale(request, response);    prepare.createActionContext(request, response);   prepare.assignDispatcherToThread();   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); } }
  相信用過 Struts2 的朋友都知道在項目搭建的過程中要在 web.xml 中配置 這個 Filter 類,它跟SpringMVC中的 DispatcherServlet 類的共同點是攔截請求然後進行一系列處理,那麽Struts2的處理就是上面這個方法,看起來很簡單對嗎?事實上就是很簡單,那麽繼續看:

    首先請求進入Tomcat 容器,由Tomcat創建 ServletRequest 和 ServletResponse 以及 FilterChain 對象並傳入 doFilter(req,res,chain)。首先把 req 和 res 進行向下轉型,接著判斷web容器中是否還有過濾器,如果有就執行下一個過濾器,沒有就進行請求資源:

  ·  ①:設置字符集編碼和環境

     ②:創建 ActionContext 對象,使用的是 PrepareOperations.java 中的方法。

     ③:創建一個 Dispatcher 對象保存在當前線程中,如果該線程中存在直接返回,如果沒有就創建一個並保存。

     ④:包裝 HttPServletRequest 對象,如果是該請求是上傳文件包裝成Struts2中的 MultiPartRequestWrapper 對象,否者是 StrutsRequestWrapper 對象,然後把包裝對象保存在 ActionContext 的 Map 集合中。

     ⑤:通過 URI 截取判斷是請求靜態資源還是請求Action,如果是請求靜態資源直接返回 NULL,否則就在 request 中尋找 Action ,在Struts2中 request 對象是保存在值棧中的,如果存在就直接返回,沒有就創建一個保存在request中並返回。

     ⑥ - ⑦:如果是請求靜態資源,就執行 executeStaticResourceRequest() 方法,否則就執行 executeAction() 方法。

     ⑧:請求已經結束了,因為Struts2是多例,最後要銷毀資源,比如 ActionContext,Dispatcher。

   上面 從第 ④ 步到最後的源碼都會貼出來,因為代碼眾多,所以這裏只貼出部分幫助理解:

  代碼先就放在這裏,日後有時間再寫出解釋。

  ④:

org.apache.struts2.dispatcher.ng.PrepareOperations

public
HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException { HttpServletRequest request = oldRequest; try { // Wrap request first, just in case it is multipart/form-data // parameters might not be accessible through before encoding (ww-1278) request = dispatcher.wrapRequest(request); ServletActionContext.setRequest(request); } catch (IOException e) { throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e); } return request; }

org.apache.struts2.dispatcher.Dispatcher.wrapRequest(HttpServletRequest)
public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException { // don‘t wrap more than once if (request instanceof StrutsRequestWrapper) { return request; } String content_type = request.getContentType(); if (content_type != null && content_type.contains("multipart/form-data")) { MultiPartRequest mpr = getMultiPartRequest(); LocaleProvider provider = getContainer().getInstance(LocaleProvider.class); request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup); } else { request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup); } return request; }

 

 ⑤:

org.apache.struts2.dispatcher.ng.PrepareOperations.findActionMapping(HttpServletRequest, HttpServletResponse, boolean)

public
ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) { ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY); if (mapping == null || forceLookup) { try { mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager()); if (mapping != null) { request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping); } } catch (Exception ex) { if (dispatcher.isHandleException() || dispatcher.isDevMode()) { dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex); } } } return mapping; }

org.apache.struts2.dispatcher.mapper.DefaultActionMapper.getMapping(HttpServletRequest, ConfigurationManager)
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); }

  ⑥:

  

 
org.apache.struts2.dispatcher.ng.ExecuteOperations.executeStaticResourceRequest(HttpServletRequest, HttpServletResponse)

public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // there is no action in this request, should we look for a static resource? String resourcePath = RequestUtils.getServletPath(request); if ("".equals(resourcePath) && null != request.getPathInfo()) { resourcePath = request.getPathInfo(); } StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class); if (staticResourceLoader.canHandle(resourcePath)) { staticResourceLoader.findStaticResource(resourcePath, request, response); // The framework did its job here return true; } else { // this is a normal request, let it pass through return false; } }
org.apache.struts2.dispatcher.DefaultStaticContentLoader.findStaticResource(String, HttpServletRequest, HttpServletResponse)
public void findStaticResource(String path, HttpServletRequest request, HttpServletResponse response) throws IOException { String name = cleanupPath(path); for (String pathPrefix : pathPrefixes) { URL resourceUrl = findResource(buildPath(name, pathPrefix)); if (resourceUrl != null) { InputStream is = null; try { //check that the resource path is under the pathPrefix path String pathEnding = buildPath(name, pathPrefix); if (resourceUrl.getFile().endsWith(pathEnding)) is = resourceUrl.openStream(); } catch (IOException ex) { // just ignore it continue; } //not inside the try block, as this could throw IOExceptions also if (is != null) { process(is, path, request, response); return; } } } response.sendError(HttpServletResponse.SC_NOT_FOUND); }

  ⑦:

  org.apache.struts2.dispatcher.Dispatcher.serviceAction(HttpServletRequest, HttpServletResponse, ActionMapping)

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
            throws ServletException {

        Map<String, Object> extraContext = createContextMap(request, response, mapping);

        // 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();

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

            // 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) {
            logConfigurationException(request, e);
            sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
        } catch (Exception e) {
            if (handleException || devMode) {
                sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            } else {
                throw new ServletException(e);
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

com.opensymphony.xwork2.DefaultActionProxy.execute()

public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        ActionContext.setContext(invocation.getInvocationContext());

        String retCode = null;

        String profileKey = "execute: ";
        try {
            UtilTimerStack.push(profileKey);

            retCode = invocation.invoke();
        } finally {
            if (cleanupContext) {
                ActionContext.setContext(nestedContext);
            }
            UtilTimerStack.pop(profileKey);
        }

        return retCode;
    }

com.opensymphony.xwork2.DefaultActionInvocation.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 = 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) {
                    LOG.trace("Executing PreResultListeners for result [#0]", result);

                    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);
        }
    }

  ⑧:

  org.apache.struts2.dispatcher.ng.PrepareOperations.cleanupRequest(HttpServletRequest)

 public void cleanupRequest(HttpServletRequest request) {
        Integer counterVal = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
        if (counterVal != null) {
            counterVal -= 1;
            request.setAttribute(CLEANUP_RECURSION_COUNTER, counterVal);
            if (counterVal > 0 ) {
                if (log.isDebugEnabled()) {
                    log.debug("skipping cleanup counter="+counterVal);
                }
                return;
            }
        }
        // always clean up the thread request, even if an action hasn‘t been executed
        try {
            dispatcher.cleanUpRequest(request);
        } finally {
            ActionContext.setContext(null);
            Dispatcher.setInstance(null);
        }
    }

Struts2運行流程-源碼剖析