SpringBoot源码学习之路(七、SpringBoot中对SpringMVC的自动配置)

SpringMVC自动配置

SpringBoot官方文档参考。

一. Spring MVC auto-configuration

对于SpringMVC的自动配置下面只是介绍了部分,如果想要了解更多Boot对SpringMVC的默认配置可以查阅源码结合官方文档了解。
源码位置:
spring-boot-autoconfigure.jar
———->>> org.springframework.boot.autoconfigure.web
———————->>>WebMvcAutoConfiguration.class
这些类都是Springboot对web场景的自动配置。
当然如果想要了解其他模块的自动配置,也是使用相同的方式去找XxxAutoConfiguratin.class 对应源码即可。

Spring Boot 自动配置好了SpringMVC,收先要了解SpringBoot对SpringMVC自动配置的内容,需要先来阅读一部分官方文档。

The auto-configuration adds the following features on top of Spring’s defaults:

  1. Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver
    beans.
  2. Support for serving static resources, including support for WebJars
    (see below).
  3. Automatic registration of Converter, GenericConverter, Formatter
    beans.
  4. Support for HttpMessageConverters (see below).
  5. Automatic registration of MessageCodesResolver (see below).
  6. Static index.html support.
  7. Custom Favicon support (see below).
  8. Automatic use of a ConfigurableWebBindingInitializer bean (see
    below).

可以看出, SpringBoot默认配置了SpringMVC:

  1. 引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。
  2. 对静态资源的支持,包括对WebJars的支持。
  3. 自动注册Converter,GenericConverter,Formatter beans。
  4. 对HttpMessageConverters的支持。
  5. 自动注册MessageCodeResolver。
  6. 对静态index.html的支持。
  7. 对自定义Favicon的支持。
  8. 字段使用 ConfigurableWebBindingInitializer bean

注意点(重点结论):
(1)自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(是转发还是重定向)页面);
(2)ContentNegotiatingViewResolver:组合所有的视图解析器的;
(3)如何定制自己的视图解析器?我们可以自己给容器中添加一个视图解析器,SpringBoot会自动的将其组合进来;因为从源码分析可以返现ContentNegotiatingViewResolver所组合的视图解析器都是从容器中获取的。


SpringBoot配置了Converter, GenericConverter, Formatter 等beans。
(1):Converter(转换器):类型转换使用Converter(将页面参数自动转化为需要的类型,如1,2017-01-01分别转换为int,Date类型);
(2):Formatter (格式化器) ; 2017.12.17 === > Date;

分析一个日期格式化器部分源码:

        @Bean
        //在文件中配置日期格式化的规则,日期格式化器才会生效
        @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
        public Formatter<Date> dateFormatter() {
            return new DateFormatter(this.mvcProperties.getDateFormat());
        }

从源码中我们可以发现,仅有在配置文件中配置了,SpringBoot配置的日期格式化器才会生效。同时通过格式化器的注解@Bean可以发现该组件在容器中,所以当我们自己需要自定义的格式化器,只需要将其加入容器中即可。(@Bena

(3):再来分析下HttpMessageConverters,其主要作用是SpringMVC用来转换Http请求和响应的;User —> Json
看看WebMvcAutoConfiguration.class中的函数:

 public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, @Lazy HttpMessageConverters messageConverters, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
            this.resourceProperties = resourceProperties;
            this.mvcProperties = mvcProperties;
            this.beanFactory = beanFactory;
            this.messageConverters = messageConverters;
            this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
        }

源码方法参数中@Lazy HttpMessageConverters messageConverters可以看出messageConverters是通过容器懒加载获得的,所以也可以得出一个结论:要自定义消息转换器,只需要自己给容器中添加自定义的HttpMessageConverter即可。

(4):ConfigurableWebBindingInitializer : 其主要作用就是 初始化WebDataBinder;将请求的参数转化为对应的JavaBean,并且会结合上面的类型、格式转换一起使用。
查看WebMvcAutoConfiguration.class中函数源码:

  protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
            try {
                //从容器中获取
                return (ConfigurableWebBindingInitializer)this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
            } catch (NoSuchBeanDefinitionException var2) {
                return super.getConfigurableWebBindingInitializer();
            }
        }

可以发现ConfigurableWebBindingInitializer是从容器(beanFactory)中获取到的,所以我们可以配置一个ConfigurableWebBindingInitializer来替换默认的,只需要在容器中添加一个我们自定义的转换器即可。


二、扩展SpringMVC

我们再看一段springBoot官方文档关于SpringMvc自动配置的描述。


If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.


其大体翻译就是:
1. 如果保留Spring Boot MVC特性,你只需添加(拓展)其他的额外的MVC配置(拦截器,格式化处 理器,视图控制器等)。你可以添加自己的WebMvcConfigurerAdapter类型 的 @Configuration 类,而不需要注解@EnableWebMvc
2. 如果希望使用自定义 的 RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver,你可以声明一 个WebMvcRegistrationsAdapter实例提供这些组件。
3. 如果想全面控制Spring MVC,你可以添加自己的@Configuration ,并使 用 @EnableWebMvc 注解。


现在我们尝试拓展一下SpringMvc的功能,要求来一个视图映射,将/hello请求映射视图success中,并且来一个拦截器拦截所有/hello请求。
这个功能如果是在springmvc中实现是这样的:

    <!-- 视图映射 -->
    <mvc:view-controller path="/hello" view-name="success"/>
    <!-- SpringMVC拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/hello"/>
            <bean></bean>
        </mvc:interceptor>
    </mvc:interceptors>

那根据上面SpringBoot的研究,在SpringBoot中该如何实现这个功能呢?

根据 上面原文的翻译,要拓展一个这样的功能大体的实现步骤是这样的:
①、编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;
②不能标注@EnableWebMvc;
这样就是既保留了所有的自动配置,也能用我们扩展的配置;

首先找到WebMvcConfigurerAdapter.class源码,发现其实是一个抽象类,我们只需要实现我们要拓展的方法即可:

@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {

    //这就是配置视图映射的方法 
     public void addViewControllers(ViewControllerRegistry registry) {
    }

    //这就是配置拦截器的方法
     public void addInterceptors(InterceptorRegistry registry) {
    }
}

下面就来实现我们的拓展(只拓展了视图映射功能,拦截器功能你们自行实现哈):

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
       // super.addViewControllers(registry);
        //浏览器发送 /hello请求来到 success
        registry.addViewController("/hello").setViewName("success");
    }
}

拓展的原理:
1)、WebMvcAutoConfiguration是SpringMVC的自动配置类;

​2)、自动配置类在做其他自动配置时会导入@Import(EnableWebMvcConfiguration.class)

  @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})

可以看出EnableWebMvcConfiguration.class是自动配置类的一个内部类。并且继承了DelegatingWebMvcConfiguration

 @Configuration
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration

再来看看DelegatingWebMvcConfiguration源码中的函数setConfigurers

      //从容器中获取所有的WebMvcConfigurer
      @Autowired(required = false)
      public void setConfigurers(List<WebMvcConfigurer> configurers) {
          if (!CollectionUtils.isEmpty(configurers)) {
              this.configurers.addWebMvcConfigurers(configurers);
              }
          }
    }

函数中setConfigurers中可以看到,其将容器中所有的WebMvcConfigurer配置都获取到了。
3)、容器中所有的WebMvcConfigurer都会一起起作用;
4)、我们的配置类也会被调用;
最终得到的效果就是:SpringMVC的自动配置和我们的扩展配置都会起作用;


三、全面控制SpringMVC配置

SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了。
从上面的官方文档翻译可以得知,我们只需要在配置类中添加@EnableWebMvc即可。

如我们将上面拓展的配置加上@EnableWebMvc注解;

@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //浏览器发送 /hello请求来到 success
        registry.addViewController("/hello").setViewName("success");
    }
}

现在可以再启动项目就可以发现,现在springmvc的自动配置都失效了(静态映射、webjars等功能)。


全面控制的原理:
为什么@EnableWebMvc自动配置就失效了?
(1)观察其源码:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented

public @interface EnableWebMvc {
}

(2)发现@EnableWebMvc的核心就是导入@Import({DelegatingWebMvcConfiguration.class})而发现DelegatingWebMvcConfiguration又是继承了WebMvcConfigurationSupport类。

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport

(3)再回头看看springBoot对MVC的自动配置类WebMvcAutoConfiguration的源码中:

@Configuration
@ConditionalOnWebApplication( type = Type.SERVLET )
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//当不存在'WebMvcConfigurationSupport.class'的时候自动配置才生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
        .....略
}

从自动配置类WebMvcAutoConfiguration可以看出当且仅当不存在WebMvcConfigurationSupport.class的时候自动配置才生效,但是因为我们将拓展的配置类加上了@EnableWebMvc导致存在类WebMvcConfigurationSupport.class
(4)因为导入的WebMvcConfigurationSupport只有SpringMVC最基本的功能,而SpringMvc的其他自动配置功能失效,所以被我们的配置全面控制。


四、总结

1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(如ViewResolver)将用户配置的和自己默认的组合起来;

2)、用户可以借助添加 WebMvcConfigurerAdapter类型的 @Configuration 类,而不需要注解@EnableWebMvc来拓展Springmvc的自定义配置。

3)、用户可以在配置类加上了@EnableWebMvc注解,实现全面控制SpringMvc配置。

4)、整个SpringBoot框架中(不仅仅是针对web模块),在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置和有很多的’有很多的xxxCustomizer帮助我们进行定制配置’帮助我们进行定制配置。这些在后期会慢慢遇上。

猜你喜欢

转载自blog.csdn.net/qq_33404395/article/details/81147019