Springboot2.0--WebMvcConfigurer詳解

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章