zoukankan      html  css  js  c++  java
  • Spring MVC详细源码解析(下篇)

    至此,第二步已经全部结束,接下来要接触到HandlerAdapter。

    第四步:

    再次回到DispatcherServlet类的doDispatch方法,继续往下走,进到getHandlerAdapter方法中。

    在DispatcherServlet类中维护了一个名为handlerAdapters的List集合,里面保存了所有的HandlerAdapter(处理映射器),Spring MVC默认给我们提供了三个:RequestMappingHandlerAdapter、HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter。

    我们先复习一下Spring MVC中构建Hanlder(处理器)的四种方法:

    1. 实现Controller接口(或继承AbstractHandler抽象类)。
    2. 实现HttpRequestHandler接口。
    3. 使用@RequestMapping注解。
    4. 继承HttpServlet(实现原始Servlet接口)。

    Spring MVC中共有四个处理器适配器:

    1. RequestMappingHandlerAdapter:适配通过@RequestMapping注解创建的Handler。

    2. HttpRequestHandlerAdapter:适配通过实现HttpRequestHandler接口创建的Handler。

    3. SimpleControllerHandlerAdapter:适配通过实现Controller接口(或继承AbstractHandler抽象类)创建的Handler。

    4. SimpleServletHandlerAdapter(不再默认提供):适配通过继承HttpServlet(实现原始Servlet接口)创建的Handler。

    // 决定使用哪个HandlerAdapter(处理映射器)来处理当前Handler
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            // 遍历所有的
            for (HandlerAdapter adapter : this.handlerAdapters) {
                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");
    }
    

    可以看出其实HandlerAdapter内部也只是通过Handler的类型来判断是否支持。

    // RequestMappingHandlerAdapter类
    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }
    
    // SimpleControllerHandlerAdapter类
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }
    
    // HttpRequestHandlerAdapter类
    public boolean supports(Object handler) {
        return (handler instanceof HttpRequestHandler);
    }
    
    // SimpleServletHandlerAdapter类
    public boolean supports(Object handler) {
        return (handler instanceof Servlet);
    }
    

    再获得了指定的处理器适配器后,就该执行了该Handler了。

    第五步:

    适配器在执行Handler之前,会调用HandlerExecutionChain的applyPreHandle方法来触发前置拦截器。接着就是调用HandlerAdapter的handle方法来执行Handler,这里使用的是实现类RequestMappingHandlerAdapter类,而handle方法在其父抽象类AbstractHandlerMethodAdapter类中,最终会调用RequestMappingHandlerAdapter类的handleInternal方法。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                // 省略其他内容...
    
                // 执行前置拦截器
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
    
                // 处理器映射器执行Handler方法
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
    
                applyDefaultViewName(processedRequest, mv);
                
                // 执行后置拦截器
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                                   new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }
    
    // AbstractHandlerMethodAdapter类
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        return handleInternal(request, response, (HandlerMethod) handler);
    }
    
    // RequestMappingHandlerAdapter类
    protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav;
        checkRequest(request);
        // Execute invokeHandlerMethod in synchronized block if required.
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No HttpSession available -> no mutex necessary
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // 调用Handler方法,看名称带invkoe可知最终是使用反射调用
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    
        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }
    
        return mav;
    }
    

    然后我们需要进入invokeHandlerMethod方法,这个方法非常重要。WebDataBinderFactory对象是用来生产数据绑定器的,ModelFactory对象是用来生成Model模型的。

    InvocableHandlerMethod抽象类是是对HandlerMethod接口的扩展,增加了调用能力(doInvoke方法)。这个能力在Spring MVC可是非常非常重要的,它能够在调用的时候,把方法入参的参数都封装进来(从HTTP request里,当然借助的必然是HandlerMethodArgumentResolver)。该类中有一个HandlerMethodArgumentResolverComposite对象存储了所有的HandlerMethodArgumentResolver,用于解析Handler方法的参数。

    ServletInvocableHandlerMethod类是对InvocableHandlerMethod抽象类的扩展,它增加了返回值和响应状态码的处理,另外在ServletInvocableHandlerMethod有个内部类ConcurrentResultHandlerMethod继承于它,支持异常调用结果处理,Servlet容器下Controller在查找适配器时发起调用的最终就是ServletInvocableHandlerMethod。该类中有一个HandlerMethodReturnValueHandlerComposite对象存储了所有的HandlerMethodReturnValueHandler,用于解析Handler方法的返回值。

    RequestMappingHandlerAdapter类默认提供了27个argumentResolver和15个returnValueResolver。

    ModelAndViewContainer类可以把它定义为ModelAndView上下文的容器,它承担着整个请求过程中的数据传递工作,保存着Model和View的相关信息。

    // RequestMappingHandlerAdapter类
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    
            // 使用Handler方法构建一个ServletInvocableHandlerMethod对象,以便下面的方法调用
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            // 为ServletInvocableHandlerMethod对象设置参数解析器和返回值解析器
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            // 为ServletInvocableHandlerMethod对象设置数据绑定器工厂
            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);
    
            // 省略其他内容...
    
            // 调用Handler方法
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
    
            // 通过ModelAndViewContainer构建一个ModelAndView对象
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }
    

    继续进入到invokeAndHandle方法内,结果发现第一行代码,又调用了一个叫invokeForRequest的方法。

    // ServletInvocableHandlerMethod类
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                                Object... providedArgs) throws Exception {
    
        // 调用Handler方法并获得返回值
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        
        // 省略其他内容...
        
        try {
            // 使用返回值处理器来处理返回值
            this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }
    

    而当进入invokeForRequest方法后,发现它又调用了一个doInvoke方法。在doInvoke方法中,才是真正地通过反射调用了Handler方法,前面的方法可以说都是在做一些准备工作,例如获取方法的参数、创建可调用的方法对象、创建模型视图容器等。

    // InvocableHandlerMethod类
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                   Object... providedArgs) throws Exception {
        // 获取Handler方法的所有参数值
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 调用Handler方法
        return doInvoke(args);
    }
    
    protected Object doInvoke(Object... args) throws Exception {
        // 获取到目标Handler方法的Method对象
        Method method = getBridgedMethod();
        ReflectionUtils.makeAccessible(method);
        try {
            if (KotlinDetector.isSuspendingFunction(method)) {
                return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
            }
            // 使用反射调用目标Handler方法
            return method.invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            assertTargetBean(method, getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        }
        catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }
    

    第六步:

    看完Handler方法的调用,就该看如何处理方法返回值了。我们再回到invokeAndHandle方法中,下一个关键方法是handleReturnValue,它的作用是使用HandlerMethodReturnValueHandler(Handler方法 返回值处理器)来处理对应的返回值,这里使用的是适配器模式。

    因为我这里返回的是一个字符串,所以应该是使用的ViewNameMethodReturnValueHandler返回值处理器。handleReturnValue方法并没有返回值,返回值(包括Model和View)将被存进ModelAndViewContainer容器中,以便后续使用。

    // HandlerMethodReturnValueHandlerComposite类
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // 选择一个合适的返回值处理器
        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        }
        // 使用指定的返回值处理器来处理返回值
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }
    
    // 选择一个合适的返回值处理器
    private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
        boolean isAsyncValue = isAsyncReturnValue(value, returnType);
        for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
            if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
                continue;
            }
            // 依次遍历返回值处理器并判断是否支持当前返回值类型
            if (handler.supportsReturnType(returnType)) {
                return handler;
            }
        }
        return null;
    }
    
    // 使用指定的返回值处理器来处理返回值
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        if (returnValue instanceof CharSequence) {
            String viewName = returnValue.toString();
            // 将视图名存入模型视图容器
            mavContainer.setViewName(viewName);
            if (isRedirectViewName(viewName)) {
                mavContainer.setRedirectModelScenario(true);
            }
        }
        else if (returnValue != null) {
            // should not happen
            throw new UnsupportedOperationException("Unexpected return type: " +
                                                    returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
        }
    }
    

    第七步:

    当HandlerMethodReturnValueHandler处理完Handler方法返回值后,就该将处理后的ModelAndView对象返回给DispatcherServlet类了。这一步就是调用RequestMappingHandlerAdapter类中invokeHandlerMethod方法的最后一行代码(即getModelAndView方法),获取ModelAndView对象。

    在getModelAndView方法中,会对Model、View做最后的处理,从ModelAndViewContainer容器中取出Model和View来构建一个ModelAndView。

    // RequestMappingHandlerAdapter类
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                                         ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
        modelFactory.updateModel(webRequest, mavContainer);
        if (mavContainer.isRequestHandled()) {
            return null;
        }
        ModelMap model = mavContainer.getModel();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            if (request != null) {
                RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
            }
        }
        return mav;
    }
    

    第八步:

    这一步是请求视图解析器解析ModelAndView对象。

    去到DispatcherServlet类中的processDispatchResult方法,可以看到中间有一个render方法,该方法的主要作用就是渲染视图了,也基本是整个流程的收尾工作。

    // DispatcherServlet类
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        // 处理器执行链
        HandlerExecutionChain mappedHandler = null;
        // 是否为多部分类型请求
        boolean multipartRequestParsed = false;
        // 管理异步请求的处理
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            // 省略其他内容...
            
            // 视图解析器解析视图
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                                   new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }
    
    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) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }
    
        // 
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("No view rendering, null ModelAndView returned.");
            }
        }
    
        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }
    
        if (mappedHandler != null) {
            // Exception (if any) is already handled..
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }
    

    在该方法中,首先是通过地域解析器解析请求,在获取到地域信息后应用于响应中。然后获取视图名,传入resolveViewName方法进行解析。resolveViewName方法的作用就是将指定的视图名通过ViewResolver(视图解析器)解析为一个视图对象,DispatcherServlet类中维护了一个存有ViewResolver(视图解析器)的List集合,但Spring MVC默认不会提供视图解析器,这里是我们自己配置了一个InternalResourceViewResolver视图解析器,它继承于UrlBasedViewResolver类,可以用来处理URL路径的视图名或者拼接视图路径等,也可以通过“redirect:”和“forward:”来指明是重定向和请求转发。

    看名字也能知道这是一个内部资源的视图解析器,注释中说了建议将视图放在/WEB-INF目录下,以便外界无法访问,只能通过Controller方法,JSP页面也常常使用该视图解析器。

    下图是自己配置的视图解析器:

    // DispatcherServlet类
    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);
    
        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // We need to resolve the view name.
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                                           "' in servlet with name '" + getServletName() + "'");
            }
        }
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                                           "View object in servlet with name '" + getServletName() + "'");
            }
        }
    
        // Delegate to the View object for rendering.
        if (logger.isTraceEnabled()) {
            logger.trace("Rendering view [" + view + "] ");
        }
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
        }
    }
    
    // 将指定的视图名解析为一个视图对象
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
                                   Locale locale, HttpServletRequest request) throws Exception {
    	// 依次遍历每个视图解析器来尝试解析该视图名
        if (this.viewResolvers != null) {
            for (ViewResolver viewResolver : this.viewResolvers) {
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }
    

    当选定一个视图解析器后,就执行其resolveViewName方法,该方法在InternalResourceViewResolver的间接父类AbstractCachingViewResolver抽象类中。createView方法来负责创建View对象,如果解析成功就会将其存入缓存viewAccessCache中,以后相同视图名就直接从缓存拿。

    // AbstractCachingViewResolver抽象类
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        if (!isCache()) {
            return createView(viewName, locale);
        }
        else {
            // 根据视图名获取缓存Key
            Object cacheKey = getCacheKey(viewName, locale);
            // 从缓存中获取对应的View对象(视图对象)
            View view = this.viewAccessCache.get(cacheKey);
            // 如果缓存中不存在才来创建
            if (view == null) {
                synchronized (this.viewCreationCache) {
                    view = this.viewCreationCache.get(cacheKey);
                    if (view == null) {
                        // 依靠子类来创建View对象(视图对象)
                        view = createView(viewName, locale);
                        if (view == null && this.cacheUnresolved) {
                            view = UNRESOLVED_VIEW;
                        }
                        // 将创建的View对象存入缓存中
                        if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
                            this.viewAccessCache.put(cacheKey, view);
                            this.viewCreationCache.put(cacheKey, view);
                        }
                    }
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace(formatKey(cacheKey) + "served from cache");
                }
            }
            return (view != UNRESOLVED_VIEW ? view : null);
        }
    }
    

    createView方法在其子类UrlBasedViewResolver类中,会根据不同的情况(重定向或请求转发)解析视图名。

    // UrlBasedViewResolver类
    protected View createView(String viewName, Locale locale) throws Exception {
        // 判断当前视图解析器是否支持处理该视图名,如果不支持就返回一个null,这样后续就能交给下一个视图解析器再来尝试
        if (!canHandle(viewName, locale)) {
            return null;
        }
    
        // 处理redirect:(重定向)的情况
        if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            RedirectView view = new RedirectView(redirectUrl,
                                                 isRedirectContextRelative(), isRedirectHttp10Compatible());
            String[] hosts = getRedirectHosts();
            if (hosts != null) {
                view.setHosts(hosts);
            }
            return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
        }
    
        // 处理forward:(请求转发)的情况
        if (viewName.startsWith(FORWARD_URL_PREFIX)) {
            String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
            InternalResourceView view = new InternalResourceView(forwardUrl);
            return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
        }
    
        // Else fall back to superclass implementation: calling loadView.
        return super.createView(viewName, locale);
    }
    
    // AbstractCachingViewResolver抽象类
    protected View createView(String viewName, Locale locale) throws Exception {
        return loadView(viewName, locale);
    }
    

    在loadView方法中调用buildView方法来真正地创建视图对象,初始化了一个视图对象,然后使用指定的前缀和后缀加上视图名拼接一个URL。

    // UrlBasedViewResolver类
    protected View loadView(String viewName, Locale locale) throws Exception {
        // 创建一个视图实例,这里是调用的InternalResourceViewResolver类中的方法
        AbstractUrlBasedView view = buildView(viewName);
        View result = applyLifecycleMethods(viewName, view);
        return (view.checkResource(locale) ? result : null);
    }
    
    // InternalResourceViewResolver类
    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        // 这里又调用的父类UrlBasedViewResolver类中的方法
        InternalResourceView view = (InternalResourceView) super.buildView(viewName);
        if (this.alwaysInclude != null) {
            view.setAlwaysInclude(this.alwaysInclude);
        }
        view.setPreventDispatchLoop(true);
        return view;
    }
    
    // UrlBasedViewResolver类
    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        // 初始化了一个InternalResourceView对象
        AbstractUrlBasedView view = instantiateView();
        // 为该视图设置URL,使用前缀+视图名+后缀的形式拼接
        view.setUrl(getPrefix() + viewName + getSuffix());
        view.setAttributesMap(getAttributesMap());
    
        // 下面都是一些参数设置
        String contentType = getContentType();
        if (contentType != null) {
            view.setContentType(contentType);
        }
    
        String requestContextAttribute = getRequestContextAttribute();
        if (requestContextAttribute != null) {
            view.setRequestContextAttribute(requestContextAttribute);
        }
    
        Boolean exposePathVariables = getExposePathVariables();
        if (exposePathVariables != null) {
            view.setExposePathVariables(exposePathVariables);
        }
        Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
        if (exposeContextBeansAsAttributes != null) {
            view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
        }
        String[] exposedContextBeanNames = getExposedContextBeanNames();
        if (exposedContextBeanNames != null) {
            view.setExposedContextBeanNames(exposedContextBeanNames);
        }
    
        return view;
    }
    

    第九步:

    当buildView方法创建了一个View对象后,就一步步将该对象返回至DispatcherServelt类的render方法中。然后接下来进行最后一步,渲染视图。

    // DispatcherServelt类
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        Locale locale =
            (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
        response.setLocale(locale);
    
        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // View对象将返回至该方法中
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                                           "' in servlet with name '" + getServletName() + "'");
            }
        }
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                                           "View object in servlet with name '" + getServletName() + "'");
            }
        }
    
        // Delegate to the View object for rendering.
        if (logger.isTraceEnabled()) {
            logger.trace("Rendering view [" + view + "] ");
        }
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            // 渲染视图
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
        }
    }
    

    第十步:

    渲染视图还是在render方法中,不过是View对象中的render方法。最后一行的renderMergedOutputModel方法是真正执行渲染输出的方法,作用是渲染给定模型的内部资源。

    通过Model对象为视图页面参数赋值,方式是将Model对象的参数依次设置为请求的attribute属性即可,因为在视图页面中会通过请求的attribute属性值来填充页面。

    最后就是通过请求转发的方式将请求分发至对应页面。至此,Spring MVC对于请求的处理流程也就结束了!

    // InternalResourceView类
    public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
                       HttpServletResponse response) throws Exception {
    
        // 省略其他内容...
    
        // 创建一个包含动态值和静态属性的组合输出映射,	动态值优先于静态属性
        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
        // 为渲染准备给定的响应
        prepareResponse(request, response);
        renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
    }
    
    // 渲染给定模型的内部资源
    protected void renderMergedOutputModel(
        Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 根据Model对象为视图页面设值,方式其实是通过为请求设置attribute参数的形式
        exposeModelAsRequestAttributes(model, request);
        exposeHelpers(request);
    
        // 决定请求分派器的路径
        String dispatcherPath = prepareForRendering(request, response);
    
        // 获取请求调度器
        RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
        if (rd == null) {
            throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
                                       "]: Check that the corresponding file exists within your web application archive!");
        }
    
        // include方式
        if (useInclude(request, response)) {
            response.setContentType(getContentType());
            if (logger.isDebugEnabled()) {
                logger.debug("Including [" + getUrl() + "]");
            }
            rd.include(request, response);
        }
        // forward方式 
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Forwarding to [" + getUrl() + "]");
            }
            // 根据URL路径进行请求转发
            rd.forward(request, response);
        }
    }
    
  • 相关阅读:
    odoo国际化翻译
    Odoo创建基础模块和相关内容
    odoo权限管理(二.记录管理)
    推荐的 MongoDB 安装文档
    Python Spider 抓取今日头条街拍美图
    Python Spider 抓取猫眼电影TOP100
    如何选择 SQL Server 数据库跟操作系统版本
    Mongodb 折腾笔记
    MySQLReport
    kernel TCP time wait bucket table overflow
  • 原文地址:https://www.cnblogs.com/SunnyGao/p/14153237.html
Copyright © 2011-2022 走看看