WebMvcConfigurer是spring內部配置的一種方式,使用JavaBean的方式代替傳統的xml配置;也可以自定義擴展配置類,實現方式是繼承WebMvcConfigurer接口;WebMvcConfigurer其實就是一個接口,具體的配置是由實現類來決定的,現在會有兩個問題,具體的實現類有哪些?這些實現類是如何加載到容器之中並生效的?帶着這兩個問題開啓我們源碼的探索之旅。
WebMvcConfigurer具體實現類
1.系統自帶實現類
-
WebMvcAutoConfigurationAdapter是Spring的主要配置類(會集成其它配置類到當前類),幾乎所有的缺省配置都是在此類中配置,此配置類的優先級是0
-
SpringDataWebConfiguration一些系統配置類,暫無仔細研究,此配置的優先級是最高的
-
WebMvcConfigurerComposite此類是一個委託代理類,在DelegatingWebMvcConfiguration類中實例化,並將系統自帶或者自定義的配置類注入到成員變量delegates之中。
2.自定義配置實現類
自定義實現類需要實現WebMvcConfigurer接口,優先級默認介於上述兩個系統自帶配置類,可以通過@Order註解或者Order接口來調整優先級
自定義配置文件示例如下(以配置路由規則方法爲例):
@Configuration
@EnableConfigurationProperties(WebProperties.class)
public class WebAutoConfiguration implements WebMvcConfigurer {
@Autowired
private WebProperties webProperties;
/**
* 配置路由規則
*
* @param configurer
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
AntPathMatcher matcher = new AntPathMatcher();
//區分大小寫,默認true
matcher.setCaseSensitive(webProperties.getPath().isCaseSensitive());
//是否去除前後空格,默認false
matcher.setTrimTokens(webProperties.getPath().isTrimTokens());
//分隔符
matcher.setPathSeparator(CharacterUtils.PATH_SEPARATOR);
//是否緩存匹配規則,默認null等於true
matcher.setCachePatterns(webProperties.getPath().isCachePatterns());
//設置路由匹配規則
configurer.setPathMatcher(matcher);
//設置URL末尾是否支持斜槓,默認true,如/a/b/有效,/a/b也有效
configurer.setUseTrailingSlashMatch(webProperties.getPath().isUseTrailingSlashMatch());
//給所有的接口統一添加前綴
configurer.addPathPrefix(webProperties.getPath().getPrefix(), c -> {
if (c.isAnnotationPresent(RestController.class) || c.isAnnotationPresent(Controller.class)) {
return true;
}
return false;
});
}
}
系統自帶配置實現類和自定義配置實現類如何生效?
DelegatingWebMvcConfiguration是對Spring MVC進行配置的一個代理類,它結合缺省配置和用戶自定義配置最終確定使用的配置。
DelegatingWebMvcConfiguration繼承自WebMvcConfigurationSupport,而WebMvcConfigurationSupport爲Spring MVC提供缺省配置,它提供的就是上面提到的缺省配置。
看如下源碼,DelegatingWebMvcConfiguration代理類會創建一個WebMvcConfigurerComposite代理類,並將容器之中的缺省配置類和自定義配置類注入到代理類之中;
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
//創建代理類實例對象
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
//將容器之中實現了WebMvcConfigurer接口的缺省配置類和自定義配置類注入到參數configurers之中
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
//將配置類添加到代理類屬性List集合中
this.configurers.addWebMvcConfigurers(configurers);
}
}
...
}
看下代理類WebMvcConfigurerComposite的源碼:
/**
* 代理類繼承WebMvcConfigurer接口,但是它是在DelegatingWebMvcConfiguration類中通過
* new 實例化的對象,所以不會注入到代理類之中
**/
class WebMvcConfigurerComposite implements WebMvcConfigurer {
private final List<WebMvcConfigurer> delegates = new ArrayList<>();
/**
* 將WebMvcConfigurer實現類的bean添加到代理類集合
**/
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.delegates.addAll(configurers);
}
}
/**
* 將路由規則配置添加到每個實現類中
**/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.configurePathMatch(configurer);
}
}
...
}
EnableWebMvcConfiguration配置類繼承了DelegatingWebMvcConfiguration代理類,類似@EnableWebMvc註解的功能,代理類中初始化配置的方法幾乎都是在這個類中通過super來調用啓動的;如下是調用代理類DelegatingWebMvcConfiguration獲取控制器方法進行初始化的方法:
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
resourceUrlProvider);
}
父類DelegatingWebMvcConfiguration(WebMvcConfigurationSupport)的requestMappingHandlerMapping方法:
/**
* Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
* requests to annotated controllers.
*/
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
//初始化RequestMappingHandlerMapping類,初始化完成後會調用afterPropertiesSet方法
//接下來就是加載容器中所有的控制器方法信息,將RequestMappingInfo和HandlerMethod方法註冊入
//緩存
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer configurer = getPathMatchConfigurer();
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
mapping.setUrlPathHelper(pathHelper);
}
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
mapping.setPathMatcher(pathMatcher);
}
Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
if (pathPrefixes != null) {
mapping.setPathPrefixes(pathPrefixes);
}
return mapping;
}
具體的缺省配置基本上都是在代理類DelegatingWebMvcConfiguration的父類WebMvcConfigurationSupport中實現的,像RequestMappingHandlerMapping初始化、RequestMappingHandlerAdapter適配器類初始化等等;
WebMvcAutoConfigurationAdapter是一個適配器類,使用@Import註解將EnableWebMvcConfiguration配置引入,可以說是spring 缺省配置的一個集合;
WebMvcAutoConfiguration是一個自動化配置類,會在bean WebMvcConfigurationSupport不存在的時候初始化,所以這也是我們實現自定義配置的時候爲什麼不繼承WebMvcConfigurationSupport類的原因;
GitHub地址:https://github.com/mingyang66/spring-parent/tree/master/doc/base