zoukankan      html  css  js  c++  java
  • SpringMVC源码阅读ViewResolver如何处理ContentNegotiatingViewResolver(九)

    接口

       public interface ViewResolver {
            /**
             * 通过viewName和locale查找View
             * @param viewName
             * @param locale
             * @return
             * @throws Exception
             */
            @Nullable
            View resolveViewName(String viewName, Locale locale) throws Exception;
        }

    类图

     spring boot 默认用了以上几种

    ContentNegotiatingViewResolver 为排列的第一个 他内部什么都不做只是维护了多个Resolver加了排序  所以我们通过这个为入口开始看源码

    ContentNegotiatingViewResolver

    initServletContext

    org.springframework.web.servlet.view.ContentNegotiatingViewResolver#initServletContext

        protected void initServletContext(ServletContext servletContext) {
            /**
             * 从容器中获得所有ViewResolver  我们可以自定义我们的ViewResolver
             */
            Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
            ViewResolver viewResolver;
            if (this.viewResolvers == null) {
                this.viewResolvers = new ArrayList(matchingBeans.size());
                Iterator var3 = matchingBeans.iterator();
    
                while(var3.hasNext()) {
                    viewResolver = (ViewResolver)var3.next();
                    //排除当前对象 因为当前resolver也在容乃公器里面
                    if (this != viewResolver) {
                        this.viewResolvers.add(viewResolver);
                    }
                }
            } else {
                for(int i = 0; i < this.viewResolvers.size(); ++i) {
                    viewResolver = (ViewResolver)this.viewResolvers.get(i);
                    if (!matchingBeans.contains(viewResolver)) {
                        String name = viewResolver.getClass().getName() + i;
                        this.obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolver, name);
                    }
                }
            }
            //根据@Order注解进行优先级排序
            AnnotationAwareOrderComparator.sort(this.viewResolvers);
            this.cnmFactoryBean.setServletContext(servletContext);
        }

    DispatcherServlet

    org.springframework.web.servlet.DispatcherServlet#doDispatch

    ->

    org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    ->

    org.springframework.web.servlet.DispatcherServlet#render

    render

     protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
            //调用localeResolver
            Locale locale = this.localeResolver.resolveLocale(request);
            response.setLocale(locale);
            View view;
            if (mv.isReference()) {
                //<1>根据viewResolver获得view
                view = this.resolveViewName(mv.getViewName(), 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 {
                //如果我们ModelAndView直接返回的View则直接获取Vew
                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.isDebugEnabled()) {
                this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");
            }
    
            try {
                //解析
                view.render(mv.getModelInternal(), request, response);
            } catch (Exception var7) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var7);
                }
    
                throw var7;
            }
        }

    <1>resolveViewName

    org.springframework.web.servlet.DispatcherServlet#doDispatch

    ->

    org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    ->

    org.springframework.web.servlet.DispatcherServlet#render

    ->

    org.springframework.web.servlet.DispatcherServlet#resolveViewName

        protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
            //viewResolvers dispatcher init初始化 第一个为ContentNegotiatingViewResolver
            Iterator var5 = this.viewResolvers.iterator();
            View view;
            do {
                if (!var5.hasNext()) {
                    return null;
                }
                ViewResolver viewResolver = (ViewResolver)var5.next();
                //调用<2>viewResolver 传入viewName和locale 获得View
                view = viewResolver.resolveViewName(viewName, locale);
            } while(view == null);
    
            return view;
        }

    ContentNegotiatingViewResolver

    org.springframework.web.servlet.DispatcherServlet#doDispatch

    ->

    org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    ->

    org.springframework.web.servlet.DispatcherServlet#render

    ->

    org.springframework.web.servlet.DispatcherServlet#resolveViewName

    ->

    org.springframework.web.servlet.view.ContentNegotiatingViewResolver#resolveViewName

    <2>resolveViewName

    public View resolveViewName(String viewName, Locale locale) throws Exception {
            RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
            Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
            /**
             * Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,q=0.8,application/signed-exchange;v=b3
             * 获得request head里面的Accept值  ,号分割
             * 此为http协议里面代表客户端期望接收的数据类型
             */
            List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
            if (requestedMediaTypes != null) {
                /**
                 * <3>遍历所有ViewResolve 获得view 注意:这里可能获取到多个
                 */
                List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
                /**
                 *<5>根据requestedMediaTypes 和attrs决策出一个最优的view
                 */
                View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
                if (bestView != null) {
                    return bestView;
                }
            }
    
            String mediaTypeInfo = this.logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : "";
            if (this.useNotAcceptableStatusCode) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
                }
                //返回406的view
                /**
                 * private static final View NOT_ACCEPTABLE_VIEW = new View() {
                 *         @Nullable
                 *         public String getContentType() {
                 *             return null;
                 *         }
                 *
                 *         public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
                 *             response.setStatus(406);
                 *         }
                 *     };
                 */
                return NOT_ACCEPTABLE_VIEW;
            } else {
                this.logger.debug("View remains unresolved" + mediaTypeInfo);
                return null;
            }
        }

    <3>getCandidateViews

    org.springframework.web.servlet.DispatcherServlet#doDispatch

    ->

    org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    ->

    org.springframework.web.servlet.DispatcherServlet#render

    ->

    org.springframework.web.servlet.DispatcherServlet#resolveViewName

    ->

    org.springframework.web.servlet.view.ContentNegotiatingViewResolver#resolveViewName

    ->

    org.springframework.web.servlet.view.ContentNegotiatingViewResolver#getCandidateViews

        private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
            List<View> candidateViews = new ArrayList();
            if (this.viewResolvers != null) {
                Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
                Iterator var5 = this.viewResolvers.iterator();
    
                while(var5.hasNext()) {
                    ViewResolver viewResolver = (ViewResolver)var5.next();
                    //遍历resolver选择合适的view
                    View view = viewResolver.resolveViewName(viewName, locale);
                    if (view != null) {
                        candidateViews.add(view);
                    }
    
                    Iterator var8 = requestedMediaTypes.iterator();
    
                    while(var8.hasNext()) {
                        MediaType requestedMediaType = (MediaType)var8.next();
                        /**
                         * <4>这里主要根据客户端期待的数据类型 获得后缀 再次调用resolve生成view默认都是获取空
                         * 比如我们viewname是index  inde.jsp  index.html  index.xml
                         *   private final Set<MediaTypeFileExtensionResolver> resolvers; 默认为空 我们有需求可以给成员变量注入映射关系
                         */
                        List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
                        Iterator var11 = extensions.iterator();
    
                        while(var11.hasNext()) {
                            String extension = (String)var11.next();
                            //拼接后缀生成view
                            String viewNameWithExtension = viewName + '.' + extension;
                            view = viewResolver.resolveViewName(viewNameWithExtension, locale);
                            if (view != null) {
                                candidateViews.add(view);
                            }
                        }
                    }
                }
            }
            //是否有配置默认view 如果有配置将默认views加入进去 。比如404  html的view ajax的view
            if (!CollectionUtils.isEmpty(this.defaultViews)) {
                candidateViews.addAll(this.defaultViews);
            }
    
            return candidateViews;
        }
    }

    <4>resolveFileExtensions

    org.springframework.web.servlet.DispatcherServlet#doDispatch

    ->

    org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    ->

    org.springframework.web.servlet.DispatcherServlet#render

    ->

    org.springframework.web.servlet.DispatcherServlet#resolveViewName

    ->

    org.springframework.web.servlet.view.ContentNegotiatingViewResolver#resolveViewName

    ->

    org.springframework.web.servlet.view.ContentNegotiatingViewResolver#getCandidateViews

    ->

    org.springframework.web.accept.ContentNegotiationManager#resolveFileExtensions

     public List<String> resolveFileExtensions(MediaType mediaType) {
            Set<String> result = new LinkedHashSet();
            //默认为空 获得resolvers
            Iterator var3 = this.resolvers.iterator();
    
            while(var3.hasNext()) {
                MediaTypeFileExtensionResolver resolver = (MediaTypeFileExtensionResolver)var3.next();
                //映射关系转换
                result.addAll(resolver.resolveFileExtensions(mediaType));
            }
    
            return new ArrayList(result);
        }

    <5>candidateViews

        @Nullable
        private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) {
            Iterator var4 = candidateViews.iterator();
    
            while(var4.hasNext()) {
                //如果是重定向view 直接返回
                View candidateView = (View)var4.next();
                if (candidateView instanceof SmartView) {
                    SmartView smartView = (SmartView)candidateView;
                    if (smartView.isRedirectView()) {
                        return candidateView;
                    }
                }
            }
    
            var4 = requestedMediaTypes.iterator();
    
            while(var4.hasNext()) {
                MediaType mediaType = (MediaType)var4.next();
                Iterator var10 = candidateViews.iterator();
    
                while(var10.hasNext()) {
                    View candidateView = (View)var10.next();
                    if (StringUtils.hasText(candidateView.getContentType())) {
                        //获得view 的contentType 并做叛逆的是否符合
                        MediaType candidateContentType = MediaType.parseMediaType(candidateView.getContentType());
                        if (mediaType.isCompatibleWith(candidateContentType)) {
                            if (this.logger.isDebugEnabled()) {
                                this.logger.debug("Selected '" + mediaType + "' given " + requestedMediaTypes);
                            }
    
                            attrs.setAttribute(View.SELECTED_CONTENT_TYPE, mediaType, 0);
                            return candidateView;
                        }
                    }
                }
            }
    
            return null;
        }

    总结 

    1.ContentNegotiatingViewResolver并没有做View的查找工作 只是内部继承了其他Resolver并支持Order排序

    2.同时提供支持通过MetaInfo从多个适配View适配最合适的view 因为dispacher适配一个就立即返回

    3.我们可以直接返回view对象 如果返回一个字符串将通过ViewResolver做适配查找对应的View

     
  • 相关阅读:
    Interview with BOA
    Java Main Differences between HashMap HashTable and ConcurrentHashMap
    Java Main Differences between Java and C++
    LeetCode 33. Search in Rotated Sorted Array
    LeetCode 154. Find Minimum in Rotated Sorted Array II
    LeetCode 153. Find Minimum in Rotated Sorted Array
    LeetCode 75. Sort Colors
    LeetCode 31. Next Permutation
    LeetCode 60. Permutation Sequence
    LeetCode 216. Combination Sum III
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12228515.html
Copyright © 2011-2022 走看看