从源码层次了解SpringBoot中SpringMVC的自动配置原理

    科技2022-07-27  103

    SpringBoot中有大量的自动配置,而SpringMVC自动配置属于关键的部分

    https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications

    查看官方文档

    可看出Spring Boot 自动配置好了SpringMVC,而以上罗列的内容是SpringBoot对SpringMVC的默认配置。

    接下来,详细介绍每一条的内容:

    1)Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(所谓的渲染是指是否转发?是否重定向?))

    我们来看一下源码,首先是搜索一下WebMvcAutoConfiguration类(MVC自动配置类)

     

    进入后,再搜索ContentNegotiatingViewResolver,即视图解析器

     

    我们可以进入视图解析器,在里面找到   resolveViewName(),这是真正进行试图解析的方法

     

    在这个方法里面,有两个核心的方法:getCandidateViews()和getBestView()

    getCandidateViews(),用来获取候选视图

    getBestView(),用以获取最适合的视图

     

    我们先进入getCandidateViews()方法里面看看:

     

    因此,我们可以总结出来,ContentNegotiatingViewResolver是用以组合所有的视图解析器的。

    那我们如何定制呢:我们可以自己给容器中添加一个视图解析器,ContentNegotiatingViewResolver就会自动地将其组合进来;

    我们可以来实战看看是否真的是这么一回事。

    首先,我们先在代码中实现一个ViewResolver类型的方法,同时声明一个实现了ViewResolver接口的内部类:

     

    之后我们搜索dispatcherservlet类

     

    进入后,我们搜索doDispatch()方法。所有请求一进来,会先进入这个方法

     

    2)Support for serving static resources, including support for WebJars (see below).

    静态资源文件夹路径,即webjars(之前已讲)

    1、Automatic registration of Converter, GenericConverter, Formatter beans.

    自动注册Converter(转换器):类型转换使用。例如public String hello(User user),但是页面传来一个18(年纪)

    Formatter:格式化器:格式转化。例如2020/9/8 ==Date

    同样地,我们可以在WebMvcAutoConfiguration类中找到如下方法(当然新版没有这个方法了)

    接下来在这个类里面继续查看addFormatters()方法

     

    再进入ApplicationConversionService,寻找一下addBeans(),在这个方法里面,我们发现getBeansOfType()这个方法获取Converter这个转换器类的值

     

    getBeansOfType()这个方法我们也可以点进去看看:

    我们可以看到这个方法本质上是个容器,于是思考:我们能否自己添加转化器呢?答案是可以!自己添加的格式化器转换器,我们只需要放在容器中即可。

    1、Support for HttpMessageConverters (see below).

    HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User---Json;

    针对上述的HttpMessageConverter是如何配的呢?我们可以看看源码:

     

    首先,我们在WebMvcAutoConfiguration类中寻找HttpMessageConverters

     

    我们再看看WebMvcAutoConfigurationAdapter()这个方法,其中有一个参数如下:

    需要注意的是,如果只有一个有参构造器的情况下,每一个参数的值都是需要从容器中拿到的。相当于HttpMessageConverters如何确定值,是要从容器中拿的。因此总结出第二条:

    HttpMessageConverters 是从容器中确定;底层原理即是获取所有的HttpMessageConverter;

     

    我们来看看这个HttpMessageConverters到底是个啥,点进去!

    可以看到里面都是一些有参构造器,而这些有参构造器要么从数组取值,要么是从集合中取。

    换言之,如果自己给容器中添加HttpMessageConverter,仍是刚才那句话,只需要将自己的组件注册容器中(用@Bean或者@Component均可)

    我们可以在官方文档中查看到这点:

    绿框是说,如果我们需要添加或者定制化一些converters,那我们可以使用Spring Boot的HttpMessageConverters类

    1、Automatic registration of MessageCodesResolver (see below):定义错误代码生成规则

    上源码,先搜索MessageCodesResolver

     

    我们注意到,它还能从一个配置类里面取配置。

    我们可以点击getMessageCodesResolverFormat()进入这个配置里面窥其一二:

    这个配置返回了一个messageCodesResolverFormat,点击进入,发现有一个format

    点击这个fromat进行查看:

    发现这是一个枚举类,里面罗列的是错误码生成规则!

    1、Automatic use of a ConfigurableWebBindingInitializer bean (see below).

    找到它之后,我们分析源码,发现其实也是在容器中取值

    这告诉我们,我们可以配置一个ConfigurableWebBindingInitializer来替换默认的(要给容器中添加组件,因此需要添加到容器)

    继续分析代码,如下代码含义是指,如果拿不到值,那就返回父类的

    我们可以看看返回了父类的什么:

    原来是创造一个Initializer,这个的作用是初始化web数据绑定器,这个绑定器的功能是将请求数据绑定到JavaBean

     

    我们可以再来看看ConfigurableWebBindingInitializer

    点进去之后,找到 initBinder()方法,其作用便是初始化web数据绑定器

     

    通过以上的系列代码分析,我们可以发现:

    以上仅仅是分析了WebMVC自动配置,因此自动配置可不仅仅是这些;

    Springboot对整个web的配置,我们都可以在项目中找到:

    在web模块中发现有很多自动配置类,其都是对web方面进行的自动配置

    org.springframework.boot.autoconfigure.web:web的所有自动场景

    以上的很多内容我们很难理解,但是我们是想通过以上的分析对这种固定模式有所了解:

    即如何修改SpringBoot的默认配置,我们总结出如下的模式套路:

    注意,这个模式不仅能用于web模块,其他模块也一样受用!

    模式:

    1、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)

    如果有就用用户配置的,如果没有,才自动配置;

    如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;当然,这不是所有的情况,正如官方文档所言:

    译文:如果你想保持springboot对MVC的配置功能,并且我们又想添加一些额外的MVC配置,那我们就可以编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;而且还不能标注@EnableWebMvc

     

    接下来我们实践一下,如何扩展这个功能

    扩展与全面接管SpringMVC

    1、扩展SpringMVC

     

     

    首先我们新建一个类,例如MyConfig,编写如下代码:

    代码部分主要是实现了WebMvcConfigurer接口,这是最新的做法,视频中的做法是继承:

    而WebMvcConfigurer这个接口内部则是众多的空方法,在我们实现这个接口时,可以自定义方法的内容:

     

    在MyConfig类内部是重写了addViewControllers(),用以添加视图控制器。即我们在url输入“test”,应当访问的是“success”页面

     

     

    注意,这是扩展springmvc,既保留了所有的自动配置,也能用我们扩展的配置。那么其原理又是怎样的呢?

    原理:

    1、WebMvcAutoConfiguration是SpringMVC的自动配置类

    我们发现其中有一个WebMvcAutoConfigurationAdapter类,它是实现了WebMvcConfigurer这个接口

     

    我们重点看一下这个类的头部,有一个@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})

    因此,我们可以得出第二条重要信息:

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

    点击进入EnableWebMvcConfiguration.class类,发现其是继承了DelegatingWebMvcConfiguration这个类,

    我们看看这个父类:

    在头部有 @Autowired注解,说明方法的参数需要从容器中获取。

    setConfigurers()中定义了一个List类型的参数,而这个方法的作用便是从容器中获取所有的WebMvcConfigurer

     

    在这个类里面还有一些配置方法,要注意噢,这些都是调用的configurers的方法

    例如添加视图映射,addViewControllers(),它调的是addViewControllers()方法

     

    进入addViewControllers()看看,

    这两行关键代码的作用是将所有的WebMvcConfigurer相关配置都拿来一起调用,即一起起作用,所以我们得出第三点:

     

     3)、容器中所有的WebMvcConfigurer都会一起起作用;

     4)、我们的配置类也会被调用;

    效果:SpringMVC的自动配置和我们的扩展配置都会起作用;

    当然,仍然不要忘记还有这个条件:

    Buuuuuuuuuuuut!!!!!

    于是由此引出第三大点,即全面接管SpringMVC

    3、全面接管SpringMVC

    SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了

    我们需要在配置类中添加@EnableWebMvc即可;

     

    当添加了@EnableWebMvc注解后,相当于开启springmvc的全面接管,那么之前的springmvc的自动配置就会全部失效,实践的最好对象就是静态资源。可以简单来实践一下。

     

    我们在主程序类头部添加@EnableWebMvc注解

     

     

    控制台也对此有输出信息:

     

    那么全面接管的原理又是什么呢?

    原理:

    为什么@EnableWebMvc自动配置就失效了;

    1、@EnableWebMvc的核心

     

    里面导入了DelegatingWebMvcConfiguration.class文件,那就点进去看看吧

    我们发现这个类真的似曾相识,点这里!(P13)

     

    再来看看为什么这个类把对应模块给整失效了呢?那就需要我们再来看看WebMvcAutoConfiguration的头部了,其中有一个即为关键的语句:

    @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

    这是用来判断容器中没有这个组件的时候,这个自动配置类才生效。

     

     

    嘿嘿嘿,有点意思,因为刚才我们看到,在@EnableWebMvc这个注解里面,正是导入了WebMvcConfigurationSupport这个类

     

     

    Processed: 0.010, SQL: 8