https://www.cnblogs.com/lijinyu08/p/14613902.html

  1. Spring MVC Auto-configuration
  2. // Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。
  3. Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
  4. // 自动配置在Spring默认设置的基础上添加了以下功能:
  5. The auto-configuration adds the following features on top of Springs defaults:
  6. // 包含视图解析器
  7. Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
  8. // 支持静态资源文件夹的路径,以及webjars
  9. Support for serving static resources, including support for WebJars
  10. // 自动注册了Converter:
  11. // 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
  12. // Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
  13. Automatic registration of Converter, GenericConverter, and Formatter beans.
  14. // HttpMessageConverters
  15. // SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;
  16. Support for HttpMessageConverters (covered later in this document).
  17. // 定义错误代码生成规则的
  18. Automatic registration of MessageCodesResolver (covered later in this document).
  19. // 首页定制
  20. Static index.html support.
  21. // 图标定制
  22. Custom Favicon support (covered later in this document).
  23. // 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!
  24. Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
  25. /*
  26. 如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己
  27. 的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供
  28. RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义
  29. 实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。
  30. */
  31. If you want to keep Spring Boot MVC features and you want to add additional MVC configuration
  32. (interceptors, formatters, view controllers, and other features), you can add your own
  33. @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide
  34. custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or
  35. ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
  36. // 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
  37. If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.
  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
    • 内容协商视图解析器和BeanName视图解析器;
  • Support for serving static resources, including support for WebJars (covered later in this document)).
    • 静态资源(包括webjars);
  • Automatic registration of Converter, GenericConverter, and Formatter beans.
    • 自动注册 Converter,GenericConverter,Formatter;
  • Support for HttpMessageConverters (covered later in this document).
    • 支持 HttpMessageConverters(后续文章有内容协商原理分析);
  • Automatic registration of MessageCodesResolver (covered later in this document).
    • 自动注册 MessageCodesResolver (国际化用,少用,一般直接开发两套页面);
  • Static index.html support.
    • 静态index.html 页支持;
  • Custom Favicon support (covered later in this document).
    • 自定义Favicon;
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
    • 自动使用 ConfigurableWebBindingInitializer,(DataBinder负责将请求数据绑定到JavaBean上);

      视图解析器 - ContentNegotiatingViewResolver - 内容协商视图解析器

      自动配置了 ViewResolver,就是我们之前学习的SpringMVC的视图解析器;

即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。

我们去看看这里的源码:我们找到 WebMvcAutoConfiguration , 然后搜索ContentNegotiatingViewResolver。找到如下方法!

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
    // ContentNegotiatingViewResolver 使用所有其他视图解析器来定位视图,
    // 因此它应该具有较高的优先级
    resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return resolver;
}

我们可以点进这类看看!找到对应的解析视图的代码;

@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
    RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
    Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
    List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
    if (requestedMediaTypes != null) {
        // 获取候选的视图对象
        List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
        // 选择一个最适合的视图对象,然后把这个对象返回
        View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
        if (bestView != null) {
            return bestView;
        }
    }
    //...
}

我们继续点进去看,他是怎么获得候选的视图的呢?
可以看到,getCandidateViews 中把所有的视图解析器拿来,进行 forEach 循环,挨个解析!

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");
        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                candidateViews.add(view);
            }
            // ...
        }
    }
    if (!CollectionUtils.isEmpty(this.defaultViews)) {
        candidateViews.addAll(this.defaultViews);
    }
    return candidateViews;
}

所以得出结论:ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的。

我们再去研究下他的组合逻辑,看到有个属性 viewResolvers,看看它是在哪里进行赋值的?

@Override
protected void initServletContext(ServletContext servletContext) {
    // 这里它是从beanFactory工具中获取容器中的所有视图解析器
    // ViewRescolver.class 把所有的视图解析器来组合的
    Collection<ViewResolver> matchingBeans =
        BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
    if (this.viewResolvers == null) {
        this.viewResolvers = new ArrayList<>(matchingBeans.size());
        for (ViewResolver viewResolver : matchingBeans) {
            if (this != viewResolver) {
                this.viewResolvers.add(viewResolver);
            }
        }
    }
    //...
}

既然它是在容器中去找视图解析器,我们是否可以猜想,我们就可以去实现一个视图解析器了呢?

我们可以自己给容器中去添加一个视图解析器;这个初始化方法就会帮我们自动的将它组合进来!

时间格式化器 - FormattingConversionService

找到格式化转换器:

@Bean
@Override
public FormattingConversionService mvcConversionService() {
    Format format = this.mvcProperties.getFormat();
    WebConversionService conversionService = 
        new WebConversionService(new DateTimeFormatters()
                                 .dateFormat(format.getDate())
                                 .timeFormat(format.getTime())
                                 .dateTimeFormat(format.getDateTime()));
    addFormatters(conversionService);
    return conversionService;
}

可以看到,这里设置的时间格式是从 mvcProperties 属性文件中读取的,即我们可以在 springboot 配置文件中自定义配置的:
org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.Format

spring:
  mvc:
    format:
      date: dd/MM/yyyy
      time: HH:mm:ss
      date-time: yyyy-MM-dd HH:mm:ss