目录

23 @EnableWebMvc 的作用及源码分析

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


@EnableWebMvc的声明

@EnableWebMvc 的声明源码如下:

1
2
3
4
5
6
7
8
9
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 这里支持了激活mvc各种组件功能,其中父类中有很多@Bean注解的方法,创建了许多可能需要的组件,
// 比如RequestMappingHandlerMapping就是在这时候生成的URL-对象方法的映射,
// RequestMappingHandlerMapping实现了InitializingBean,afterPropertiesSet方法中初始化了映射关系
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

在 @EnableWebMvc 声明处,使用 @Import 注解导入了一个类,这个类是 DelegatingWebMvcConfiguration,这是一个配置类,使用了注解 @Configuration 标注。

DelegatingWebMvcConfiguration 中的配置聚合

在 DelegatingWebMvcConfiguration 中,聚合了项目中所有的 WebMvcConfigurer 实现类实例。在这个类中,有很多 @Bean 注解标注的方法,来完成一些 mvc 组件的注册。

而对这些组件的注册的一些配置,会通过调用 DelegatingWebMvcConfiguration 的一些回调方法来完成,这些回调方法中,遍历了搜集到的 WebMvcConfigurer,将对应的方法进行依次调用。

在 DelegatingWebMvcConfiguration 中,聚合 WebMvcConfigurer 实例的源码如下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
	// 这个类合成了容器中所有实例化的 WebMvcConfigurer
	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

	/** 将容器中所有实例化的 WebMvcConfigurer 根据类型注入到方法参数,将其封装起来,之后所有的配置方法中,
	 * 会遍历这里搜集到的所有的实例,依次进行配置。 */
	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}
}

在 DelegatingWebMvcConfiguration 中,声明了一个成员变量 configurers,这个类是 WebMvcConfigurerComposite,同时它也是一个 WebMvcConfigurer 的实现。

在 setConfigurers() 中,注入了子容器中所有的 WebMvcConfigurer 实例,并将其封装到 configurers 中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class WebMvcConfigurerComposite implements WebMvcConfigurer {

	private final List<WebMvcConfigurer> delegates = new ArrayList<>();


	public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.delegates.addAll(configurers);
		}
	}

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		for (WebMvcConfigurer delegate : this.delegates) {
			delegate.configureViewResolvers(registry);
		}
	}

    // 省略一系列方法实现...
}

通过 addWebMvcConfigurers() 方法,将所有的实现类,封装到成员 delegates 中。

WebMvcConfigurerComposite 本身并不提供任何组件的配置,而是通过搜集容器中注入的 WebMvcConfigurer 实例,调用各个配置方法时,通过遍历这些实例,依次调用其同名方法来进行配置。

比如 configureViewResolvers(),它会遍历所有封装的实例,依次调用这些实例的 configureViewResolvers(),来进行组件的配置。

DelegatingWebMvcConfiguration 中的组件注册

在 DelegatingWebMvcConfiguration 继承了 WebMvcConfigurationSupport,在这个类中,通过 @Bean的方式,完成了很多 mvc 相关组件的注册。

主要成员

DelegatingWebMvcConfiguration 的主要成员变量如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
	/** spring容器的上下文对象 */
	@Nullable
	private ApplicationContext applicationContext;
	/** servlet 上下文对象 */
	@Nullable
	private ServletContext servletContext;
    /** springmvc 拦截器封装 */
	@Nullable
	private List<Object> interceptors;
	/** 帮助配置HandlerMappings路径匹配类的封装 */
	@Nullable
	private PathMatchConfigurer pathMatchConfigurer;
	/** 内容协商机制 */
	@Nullable
	private ContentNegotiationManager contentNegotiationManager;
	/** 方法的参数处理 */
	@Nullable
	private List<HandlerMethodArgumentResolver> argumentResolvers;
	/** 方法的返回值处理 */
	@Nullable
	private List<HandlerMethodReturnValueHandler> returnValueHandlers;
	/** 消息转换器 */
	@Nullable
	private List<HttpMessageConverter<?>> messageConverters;
	/** cors 跨域相关 */
	@Nullable
	private Map<String, CorsConfiguration> corsConfigurations;
}

这里面封装了上下文对象、拦截器、方法参数处理、返回值处理、消息转换器等实例。

注册 RequestMappingHandlerMapping

通过 @Bean,注册了 RequestMappingHandlerMapping :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {

    @Bean
	public RequestMappingHandlerMapping requestMappingHandlerMapping() {
		// RequestMappingHandlerMapping 实现了  InitializingBean,所以它在实例化后,会调用 afterPropertiesSet() 进行初始化。

		// 创建 RequestMappingHandlerMapping。
		RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
		mapping.setOrder(0);
		// getInterceptors()中调用了钩子方法,可通过 WebMvcConfigurer进行扩展
		mapping.setInterceptors(getInterceptors());
		mapping.setContentNegotiationManager(mvcContentNegotiationManager());
		// getCorsConfigurations() 调用了钩子犯法
		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;
	}
}

在创建 RequestMappingHandlerMapping 的过程中,调用了 getInterceptors()、getCorsConfigurations() 等的回调方法。


getInterceptors() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {

	protected final Object[] getInterceptors() {
		if (this.interceptors == null) {
			InterceptorRegistry registry = new InterceptorRegistry();
			// 钩子方法,如果我们定义了一个WebMvcConfigurer,它就会回调到对应实现的方法中。
			// 遍历所有注册到容器中的WebMvcConfigurer,依次进行添加操作
			addInterceptors(registry);
			registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
			registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
			this.interceptors = registry.getInterceptors();
		}
		return this.interceptors.toArray();
	}
}

这里调用了 addInterceptors():

1
2
3
4
5
6
7
8
9
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
	// 这个类合成了容器中所有实例化的 WebMvcConfigurer
	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    @Override
	protected void addInterceptors(InterceptorRegistry registry) {
		// 遍历,依次进行添加操作。
		this.configurers.addInterceptors(registry);
	}
}

可以看到,在 addInterceptors() 中,就遍历了 DelegatingWebMvcConfiguration 中封装的所有的 WebMvcConfigurer 实例的 addInterceptors()。依次调用对 RequestMappingHandlerMapping 实例进行相关配置。


getCorsConfigurations() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
    protected final Map<String, CorsConfiguration> getCorsConfigurations() {
		if (this.corsConfigurations == null) {
			CorsRegistry registry = new CorsRegistry();
			addCorsMappings(registry);// 这里遍历了DelegatingWebMvcConfiguration中封装的 WebMvcConfigurer,依次将对应方法调用
			this.corsConfigurations = registry.getCorsConfigurations();
		}
		return this.corsConfigurations;
	}
}

同样的,这里调用了 addCorsMappings(),进入源码:

1
2
3
4
5
6
7
8
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    // 这个类合成了容器中所有实例化的 WebMvcConfigurer
	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
	@Override
	protected void addCorsMappings(CorsRegistry registry) {
		this.configurers.addCorsMappings(registry);
	}
}

同样遍历了 WebMvcConfigurer 实例,依次调用同名的配置方法。

注册 RequestMappingHandlerAdapter

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
	@Bean
	public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
		// 创建 RequestMappingHandlerAdapter
		RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
		// 这里设置的每一个参数,都触发了相应的回调,如果实现了对应方法,就会将配置注册到这个组件中,否则就注册默认的配置。
		adapter.setContentNegotiationManager(mvcContentNegotiationManager());
		adapter.setMessageConverters(getMessageConverters());
		adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
		adapter.setCustomArgumentResolvers(getArgumentResolvers());
		adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

		if (jackson2Present) {
			adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
			adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
		}

		AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
		configureAsyncSupport(configurer);
		if (configurer.getTaskExecutor() != null) {
			adapter.setTaskExecutor(configurer.getTaskExecutor());
		}
		if (configurer.getTimeout() != null) {
			adapter.setAsyncRequestTimeout(configurer.getTimeout());
		}
		// 触发了对应的回调
		adapter.setCallableInterceptors(configurer.getCallableInterceptors());
		adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

		return adapter;
	}
}

创建 RequestMappingHandlerAdapter,并对其进行一些设置,这些设置大多都是通过遍历并依次回调前面封装的 WebMvcConfigurer 对应方法进行的。

RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 都实现了接口 InitializingBean,那么在他们通过 @Bean 将自身实例注入到容器中时,也会调用 afterPropertiesSet() 做一些事情,具体做了什么,在后面文章中列举。

异常处理器 HandlerExceptionResolver

源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
	@Bean
	public HandlerExceptionResolver handlerExceptionResolver() {
		// 全局异常处理器的创建注册
		List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
		// 钩子方法,遍历依次回调封装的 WebMvcConfigurer
		configureHandlerExceptionResolvers(exceptionResolvers);
		if (exceptionResolvers.isEmpty()) {
			// 如果没有定义,则添加默认的异常处理器
			addDefaultHandlerExceptionResolvers(exceptionResolvers);
		}
		extendHandlerExceptionResolvers(exceptionResolvers);
		HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
		composite.setOrder(0);
		composite.setExceptionResolvers(exceptionResolvers);
		return composite;
	}
}

这里通过 @Bean 向容器中注入了异常处理器 HandlerExceptionResolver,如果在 WebMvcConfigurer 实现类中,配置了异常处理类,就会加载。否则就会使用默认的异常处理类 ExceptionHandlerExceptionResolver。

在 ExceptionHandlerExceptionResolver 中定义了@ControllerAdvice的支持,在 getExceptionHandlerMethod() 中,通过创建 ExceptionHandlerMethodResolver 对 @HandlerException 进行了支持。

其他

在 WebMvcConfigurationSupport 中,还有很多其他的组件注册。不再列举。