天天看點

JAVA面試題之-SpringMVC的執行流程

JAVA面試題之-SpringMVC的執行流程

1. 前言:
在java的面試過程中,如果是讨論架構方面的話,這個問題被問到的幾率就很大;
 身邊朋友在面試的時候也會時不時的碰到,固總結在此。
           
2.概念:
那什麼是springMVC呢?
 它其實是一種我們做javaWeb開發的一種架構;包括MVC三個層次的架構;
 M:modle:業務模型(也就是sevice+do/mapper層)
 V:View,視圖層(如jsp等前端顯示層)
 C:Controller,控制器(sevlet/javabean);
           

3.SpringMVC的執行流程:

JAVA面試題之-SpringMVC的執行流程
4. 源碼分析:

DispatcherServlet其實是一個Servlet,我們都知道在處理請求的時候會交給service方法進行處理;在DispatcherServlet類中繼承了FrameworkServlet,

在FrameworkServlet中的service方法中:

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
        //其他請求類型的方法,經由父類,也就是HttpServlet處理;
            super.service(request, response);
        } else {
      //如果請求類型的方法位PATCH,那麼就用processRequest進行處理;
            this.processRequest(request, response);
        }

    }
           

request.getMethod();是表示得到前端用戶端傳向伺服器傳送資料的方法;如GET,POST,HREADER,TRACE等;

而HttpMtheod中的resolve代碼如下:還是前端的那些傳送資料的方法,隻是進行了封裝;

public enum HttpMethod {
    GET,
    HEAD,
    POST,
    PUT,
    PATCH,
    DELETE,
    OPTIONS,
    TRACE;

    private static final Map<String, HttpMethod> mappings = new HashMap(16);
    private HttpMethod() {
    }

    @Nullable
    public static HttpMethod resolve(@Nullable String method) {
        return method != null ? (HttpMethod)mappings.get(method) : null;
    }
           

如果為PATCH,就會調用processRequest方法;而此方法中父類的doservice,因為子類實作了此方法,根據多态實際上使用的是DIsptcherServet中的doService方法;

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = this.buildLocaleContext(request);
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }
           

再看DispatcherServlet中的doService方法:可以看到,會執行本類中的doDispatch方法;

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        //給request中的屬性做一份快照;
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            //getAttribute ,setAttribute ,
            Enumeration attrNames = request.getAttributeNames();

            label95:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label95;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
// 如果我們沒有配置類似本地化或者主題的處理器之類的,springMVC 會使用預設的值;預設的配置檔案是,DispathcerServlet.properties;
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }

            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        try {
        //開始執行
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }
           

DispatcherServlet中的doDispatch方法;

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        //springMVC中的異步請求的管理
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                //先檢查是不是multipart類型的,比如上傳等,
                //如果是multipart類型的,就轉化為MultipartHttpServletRequest類型;
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                     //獲得目前請求的Handler
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                      // 獲得目前請求的HandlerAdapter
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    //對于header中的last-modified的處理
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
           //攔截器的preHandle方法進行處理;
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
              //真正調用handle的地方
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                      //處理預設視圖名,即添加字首字尾名
                    this.applyDefaultViewName(processedRequest, mv);
                    //攔截器postHandle進行處理
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }
                //處理最後的結果,渲染之類的都在這裡
                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
             // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
                  // Clean up any resources used by a multipart request
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }
           

//springmvc中關于dispatcherServlet中的步驟都在這;

查找請求對應的handle對象

對應的上面 mappedHandler = this.getHandler(processedRequest)代碼;

具體getHandler()方法如下:

@Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
        //周遊所有的handlerMapping
            Iterator var2 = this.handlerMappings.iterator();

            while(var2.hasNext()) {
                HandlerMapping mapping = (HandlerMapping)var2.next();
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }

        return null;
    }
           

這個方法又調用了getHandler(request);在AbstractHandlerMapping類中,

@Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 根據request,擷取handler;
        Object handler = this.getHandlerInternal(request);
        //如果沒有找到就使用預設的handler
        if (handler == null) {
            handler = this.getDefaultHandler();
        }

        if (handler == null) {
            return null;
        } else {
        //如果handler屬于String類,表示是一個bean的名稱
        //需要對照對應的bean
            if (handler instanceof String) {
                String handlerName = (String)handler;
                handler = this.obtainApplicationContext().getBean(handlerName);
            }
       //封裝handler執行鍊
            HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
            //列印相關log資訊
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Mapped to " + handler);
            } else if (this.logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
                this.logger.debug("Mapped to " + executionChain.getHandler());
            }
//判斷是否是跨域問題;
            if (this.hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
           //與跨域配置資訊映射的容器
                CorsConfiguration config = this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null;
                CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
                config = config != null ? config.combine(handlerConfig) : handlerConfig;
                //得到跨域的handeler執行鍊
                executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
            }

            return executionChain;
        }
    }

           

再看, 根據request,擷取handler,

Object handler = this.getHandlerInternal(request);

往下追,在AbstractHandlerMethodMapping類中,源碼為:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 //擷取request中的url,用來比對handler
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
        request.setAttribute(LOOKUP_PATH, lookupPath);
        this.mappingRegistry.acquireReadLock();

        HandlerMethod var4;
        try {
        //根據路徑尋找Handler;
            HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
            // 根據HandlerMethod中的bean來執行個體化Handler并添加進HandlerMethod;
            var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
        } finally {
            this.mappingRegistry.releaseReadLock();
        }

        return var4;
    }
           

根據路徑來尋找HandlerMethod;

HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);

源碼:

@Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();//直接比對
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        //如果有就直接添加到比對清單中
        if (directPathMatches != null) {
            this.addMatchingMappings(directPathMatches, matches, request);
        }
 // 還沒有比對的,就周遊所有的處理方法查找
        if (matches.isEmpty()) { 
        // No choice but to go through all 
      this.addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }
 // 找到了1比對的
        if (!matches.isEmpty()) {
             //排序之後,擷取第一個
            AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
            //如果有多個比對的,會找到第二個最适合的進行比較一下
            if (matches.size() > 1) {
                Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
                
                matches.sort(comparator);
           
                bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
                
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace(matches.size() + " matching mappings: " + matches);
                }

                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }

                AbstractHandlerMethodMapping<T>.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)matches.get(1);
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    String uri = request.getRequestURI();
                    throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }

            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            //設定request參數
            this.handleMatch(bestMatch.mapping, lookupPath, request);
            //傳回比對處理url的方法
            return bestMatch.handlerMethod;
        } else {// 最後還沒有找到,傳回null
            return this.handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
           

在AbstractHandlerMapping類中的getHandler方法中,對handler進行根據request的handler直接擷取。

(具體在AbstracHandlerMethodMapping類中)

然候如果沒有就

擷取預設Handler

如果上面沒有擷取到Handler,就會擷取預設的Handler。
如果還擷取不到就傳回null。
           

處理String類型的Handler

如果上面處理完的Handler是String類型的,
就會根據這個handlerName擷取bean。
           

封裝Handler執行鍊

上面擷取完Handler,就開始封裝執行鍊了,
就是将我們配置的攔截器加入到執行鍊中去,getHandlerExecutionChain:
           
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	//如果目前Handler不是執行鍊類型,就使用一個新的執行鍊執行個體封裝起來
    HandlerExecutionChain chain =
        (handler instanceof HandlerExecutionChain) ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
	//根據請求查詢路徑,獲得目前的url
	String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
	     //先擷取适配類型的攔截器添加進去攔截器鍊的疊代器
        Iterator var5 = this.adaptedInterceptors.iterator();
         while(var5.hasNext()) {
            HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
           //周遊攔截器,找到跟目前url對應的,添加進執行鍊中去 
               chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            } else {
            //擷取适配類型的攔截器添加進去攔截器鍊
                chain.addInterceptor(interceptor);
            }
        }
    return chain;
}
           

擷取對應請求的Handler擴充卡: HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());(DispatcherServlet類中的doDipatch()方法中)

getHandlerAdapter:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();
	//周遊所有的HandlerAdapter,找到和目前Handler比對的就傳回
    //我們這裡會比對到RequestMappingHandlerAdapter
            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }

        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
	
           

緩存的處理

也就是對last-modified的處理

執行攔截器的preHandle方法

 就是周遊所有的我們定義的interceptor,執行preHandle方法
           

使用Handler擴充卡執行目前的Handler:也就是:

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

ha.handle執行目前handlerInternal,我們這裡使用的是RequestMappingHandlerAdapter,首先會進入AbstractHandlerMethodAdapter的handle方法,然後該方法調用了本類的抽象的handlerInternal方法,RequestMappingHandlerAdapter內建了它:

protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        this.checkRequest(request);
        ModelAndView mav;
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                  // Execute invokeHandlerMethod in synchronized block if required.
                synchronized(mutex) {
                    mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = this.invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader("Cache-Control")) {
            if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            //Always prevent caching in case of session attribute management.
                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            } else {
                this.prepareResponse(response);
            }
        }
//傳回封裝的ModelAndView的mav
        return mav;
    }
           

再往下追,就是把handler封裝的handlerMethod,完成請求參數的封裝,和jason格式的轉換;

@Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        Object result;
        try {
            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }

            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }

            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
            if (asyncManager.hasConcurrentResult()) {
                result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            if (!asyncManager.isConcurrentHandlingStarted()) {
                ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
                return var15;
            }

            result = null;
        } finally {
            webRequest.requestCompleted();
        }

        return (ModelAndView)result;
    }
           

組裝預設視圖名稱

字首和字尾名都加上

執行攔截器的postHandle方法

周遊intercepter的postHandle方法。

處理最後的結果,渲染之類的
           

DispatcherServlet類中的processDispatchResult方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }
   // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
          	//渲染
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isTraceEnabled()) {
            this.logger.trace("No view rendering, null ModelAndView returned.");
        }

        if 
           // Concurrent handling started during a forward
           (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }

        }
    }
    
           

重點看下render方法,進行渲染:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
       //設定本地化
        Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();
        response.setLocale(locale);
        String viewName = mv.getViewName();
        View view;
        if (viewName != null) {
            //解析視圖名,得到視圖
            view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
            }
        } else {
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");
            }
        }

        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Rendering view [" + view + "] ");
        }

        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
                 //委托給視圖進行渲染
            view.render(mv.getModelInternal(), request, response);
        } catch (Exception var8) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Error rendering view [" + view + "]", var8);
            }

            throw var8;
        }
    }
    
           

view.render就是進行視圖的渲染,然後跳轉頁面等處理。

到這裡大概的流程就走完了。其中涉及到的東西還有很多,暫先不做詳細處理。

繼續閱讀