源碼學習系列之WebMvc自動配置原理筆記
@
web的自動配置在SpringBoot項目中是一個很重要的方面,實現代碼在spring-boot-autoconfigure工程里:
按照官方文檔的說法,SpringBoot官方的說法,Springboot的SpringMVC自動配置,主要提供了如下自動配置:
WebMvcAutoConfiguration.java這個類很關鍵,這個就是SpringBoot Springmvc自動配置的一個很關鍵的配置類
@Configuration(proxyBeanMethods = false)//指定WebMvcAutoConfiguration不代理方法
@ConditionalOnWebApplication(type = Type.SERVLET)//在web環境(selvlet)才會起效
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })//系統有有Servlet,DispatcherServlet(Spring核心的分發器),WebMvcConfigurer的情況,這個自動配置類才起效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)//系統沒有WebMvcConfigurationSupport這個類的情況,自動配置起效
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
....
}
翻下源碼,可以看到WebMvcAutoConfiguration自動配置類里還有一個WebMvcConfigurer類型的配置類,2.2.1版本是implements WebMvcConfigurer接口,1.+版本是extends WebMvcConfigurerAdapter
@Configuration(proxyBeanMethods = false)//定義為配置類
@Import(EnableWebMvcConfiguration.class)//spring底層注解,將EnableWebMvcConfiguration加到容器
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })//使WebMvcProperties、ResourceProperties配置類生效
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
....
}
1、ContentNegotiatingViewResolver
如圖,是視圖解析器的自動配置,這個類起效的情況是系統沒有ContentNegotiatingViewResolver類的情況,就調用改方法自動創建ContentNegotiatingViewResolver類
關鍵的是ContentNegotiatingViewResolver類,翻下ContentNegotiatingViewResolver類,找到如下重要的初始化方法
@Override
protected void initServletContext(ServletContext servletContext) {
//調用Spring的BeanFactoryUtils掃描容器里的所有視圖解析器ViewResolver類
Collection<ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
if (this.viewResolvers == null) {
this.viewResolvers = new ArrayList<>(matchingBeans.size());
//遍歷候選的viewResolvers,封裝到this.viewResolvers列表
for (ViewResolver viewResolver : matchingBeans) {
if (this != viewResolver) {
this.viewResolvers.add(viewResolver);
}
}
}
else {
for (int i = 0; i < this.viewResolvers.size(); i++) {
ViewResolver vr = this.viewResolvers.get(i);
if (matchingBeans.contains(vr)) {
continue;
}
String name = vr.getClass().getName() + i;
obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name);
}
}
AnnotationAwareOrderComparator.sort(this.viewResolvers);
this.cnmFactoryBean.setServletContext(servletContext);
}
所以ContentNegotiatingViewResolver類的作用就是組合所有的視圖解析器,自動配置了ViewResolver(視圖解析器作用,根據方法返回值得到視圖對象view)
往下翻代碼,可以看到resolveViewName方法,里面代碼是從this.viewResolvers獲取候選的視圖解析器,遍歷容器里所有視圖,然后通過如圖所標記的獲取候選視圖的方法,獲取候選的視圖列表,再通過getBestView獲取最合適的視圖
遍歷所有的視圖解析器對象,從視圖解析器里獲取候選的視圖,封裝成list保存
ok,跟了源碼就是只要將視圖解析器丟到Spring容器里,就可以加載到
寫個簡單的視圖解析類
DispatcherServlet是Spring核心分發器,找到doDispatch方法,debug,可以看到加的視圖解析器加載到了
2、靜態資源
也就是官方說的,如下圖所示:
翻譯過來就是支持靜態資源包括webjars的自動配置,webjars,就是以maven等等方式打成jar包的靜態資源,可以去webjars官網看看文檔:
使用的話,直接去webjars官網負責對應的配置,加到項目里就可以
路徑都是在META-INF/webjars/**
WebMvcAutoConfiguration.addResourceHandlers,這個是比較重要的資源配置方法
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
//CacheControl是Spring框架提供的http緩存
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//讀取到webjars資源,將classpath:/META-INF/resources/webjars/的webjars資源都掃描出來
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
ok,通過源碼可以知道,Springboot支持webjars和其它等等靜態資源,其它的靜態資源要放在如下目錄里,Springboot就能自動加載到
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
- classpath:/
3、自動注冊 Converter, GenericConverter, and Formatter beans.
翻譯過來就是自動注冊了 Converter, GenericConverter, and Formatter beans.
- Converter:轉換器 ,作用就是能自動進行類型轉換
eg: public String hello(User user),這是一個方法,然后前端視圖傳來的參數通過轉換器能夠根據屬性進行映射,然后進行屬性類型轉換 - Formatter :格式化器,eg:比如對前端傳來的日期2019/11/25,進行格式化處理
源碼在這里,WebMvcAutoConfiguration.addFormatters方法是添加格式化器的方法
同理,也是從Spring容器里將這幾種類拿過來
當然,還有其它的,比如WebMvcAutoConfiguration.localeResolver方法是實現i18n國際化語言支持的自動配置
@Bean
@ConditionalOnMissingBean//沒有自定義localeResolver的情況
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")//application.properties有配置了spring.mvc.locale
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
//默認使用AcceptHeaderLocaleResolver
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
具體的源碼參考我之前博客:SpringBoot系列之i18n國際化多語言支持教程,博客里面有涉及源碼的
4、支持HttpMessageConverters
HttpMessageConverters :消息轉換器,Springmvc中用來轉換http請求和響應的
源碼里是通過configureMessageConverters方法實現,很顯然也是從容器里獲取的
官方文檔里也進行了比較詳細描述,Springboot已經為我們自動配置了json的、xml的自動轉換器,當然你也可以自己添加
5、支持MessageCodesResolver
MessageCodesResolver:是消息解析器,WebMvcAutoConfiguration.getMessageCodesResolver是實現Exception異常信息格式的
WebMvcProperties配置文件定義的一個異常枚舉值
格式為如圖所示,定了了錯誤代碼是生成規則:
6、首頁支持
Springboot默認的首頁是index.html,也就是你在classpath路徑丟個index.html文件,就被Springboot默認為首頁,或者說歡迎頁
如圖示代碼,就是遍歷靜態資源文件,然后獲取index.html作為歡迎頁面
7、網站logo設置
Springboot1.+版本,是有默認的logo圖標的,2.2.1版本,經過全局搜索,沒有發現給自定義的圖標,使用的話,是直接丟在classpath路徑,文件命名為favicon.ico,不過在2.2.1代碼並沒有找到相應的配置代碼,1.+版本是有的,不過文檔還是有描述了
8、ConfigurableWebBindingInitializer 初始綁定器
跟下源碼,也是從Spring容器里獲取的,然后注意到,如果沒有這個ConfigurableWebBindingInitializer ,代碼就會調用基類的getConfigurableWebBindingInitializer
源碼,這里也是創建一個getConfigurableWebBindingInitializer
ConfigurableWebBindingInitializer 是Springboot為系統自動配置的,當然我們也可以自己定義一個ConfigurableWebBindingInitializer ,然后加載到容器里即可
初始化綁定的方法,ok,本博客簡單跟一下源碼
注意:
ok,Springboot官方文檔里還有這樣的描述,如圖所示
意思是,在使用webmvcConfigurer配置的時候,不要使用@EnableWebMvc注解,為什么不要使用呢?因為使用了@EnableWebMvc,就是實現全面接管SpringMVC自動配置,也就是說其它的自動配置都會失效,全部自己配置
原理是為什么?可以簡單跟一下源碼,如圖,SpringMVC自動配置類,有這個很關鍵的注解,這個注解的意思是@WebMvcConfigurationSupport注解不在系統時候自動配置才起效
然后為什么加了@EnableWebMvc自動配置就可以被全面接管?點一下@EnableWebMvc源碼
很顯然,DelegatingWebMvcConfiguration類extends WebMvcConfigurationSupport類,所以這也就是為什么@EnableWebMvc注解能實現全面接管自動配置的原理