SpringBoot原始碼學習之路(七、SpringBoot中對SpringMVC的自動配置)
SpringMVC自動配置
一. 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:
- Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver
beans.- Support for serving static resources, including support for WebJars
(see below).- Automatic registration of Converter, GenericConverter, Formatter
beans.- Support for HttpMessageConverters (see below).
- Automatic registration of MessageCodesResolver (see below).
- Static index.html support.
- Custom Favicon support (see below).
- Automatic use of a ConfigurableWebBindingInitializer bean (see
below).
可以看出, SpringBoot預設配置了SpringMVC:
- 引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。
- 對靜態資源的支援,包括對WebJars的支援。
- 自動註冊Converter,GenericConverter,Formatter beans。
- 對HttpMessageConverters的支援。
- 自動註冊MessageCodeResolver。
- 對靜態index.html的支援。
- 對自定義Favicon的支援。
- 欄位使用 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. 如果希望使用自定義 的 RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
或ExceptionHandlerExceptionResolver
,你可以宣告一 個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
幫助我們進行定製配置’幫助我們進行定製配置。這些在後期會慢慢遇上。