Spring Boot中SpringMvc配置类WebMvcConfigurer原理分析

WebMvcConfigurer配置类其实是Spring的Java-based配置方式的一种体现,采用JavaBean的形式来代替传统的xml配置文件形式针对框架进行个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter。

定制Spring MVC配置,需要创建一个配置类并实现WebMvcConfigurer 接口。Spring5.0之前的版本,Spring推荐的是通过继承WebMvcConfigurerAdapter的方式来扩展mvc相关配置,Sping5.0之后,由于JDK8中接口支持默认方法的新特性 ,WebMvcConfigurerAdapter已被标记为Depercated,可直接实现WebMvcConfigurer。

我们先看下两个类的关系和注释:

/**
 * Defines callback methods to customize the Java-based configuration for
 * Spring MVC enabled via {@code @EnableWebMvc}.
 *
 * <p>{@code @EnableWebMvc}-annotated configuration classes may implement
 * this interface to be called back and given a chance to customize the
 * default configuration.
 *
 * @author Rossen Stoyanchev
 * @author Keith Donald
 * @author David Syer
 * @since 3.1
 */
public interface WebMvcConfigurer {
/**
 * 一个WebMvcConfigurer的空实现类,允许子类重写它们感兴趣的方法(来进行配置扩展)
 * An implementation of {@link WebMvcConfigurer} with empty methods allowing
 * subclasses to override only the methods they're interested in.
 *
 * @author Rossen Stoyanchev
 * @since 3.1
 * 从5.0版本标记为过时。是由于WebMvcConfigurer接口类(基于Java 8版本)拥有了默认的方法,可以被直接实现而不需要通过
 * WebMvcConfigurerAdapter类进行中间的适配。 
 * @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
 * possible by a Java 8 baseline) and can be implemented directly without the
 * need for this adapter
 */
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {

从类的注释我们可以看出,从Spring5.0版本,我们可以直接实现WebMvcConfigurer而不需要通过继承WebMvcConfigurerAdapter 的方式。

那么,为何通过实现WebMvcConfigurer接口就可以自定义或者扩展mvc的配置呢

下面我们以WebMvcConfigurer的addArgumentResolvers方法为例来分析Spring是如何读取并使用我们自定义的参数解析器的。

WebMvcConfigurer的addArgumentResolvers方法定义

/** 
 * 新增一个解析器以支持自定义的Controller方法的参数类型
 * Add resolvers to support custom controller method argument types.
 * 这个方法不会覆盖内置的参数解析器,如果要修改内置的参数解析器,需要直接继承RequestMappingHandlerAdapter类,
 * <p>This does not override the built-in support for resolving handler
 * method arguments. To customize the built-in support for argument
 * resolution, configure {@link RequestMappingHandlerAdapter} directly.
 * @param resolvers initially an empty list
 */
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}

那么我们重写的这个方法是如何被Spring调用到的呢?
我们直接打开addArgumentResolvers方法的调用关系,发现被三个类调用,无论打开哪个类都可以找到最终调用的地方
在这里插入图片描述
我们打开DelegatingWebMvcConfiguration这个类,这个类是WebMvcConfigurer 的代理类,发现它的addArgumentResolvers方法是调用了成员变量configurers的addArgumentResolvers方法,查看这个成员变量,发现这个变量是通过@Autowired注解持有了所有自定义WebMvcConfigurer的子类。

到了这里,我们看到了我们自定义的WebMvcConfigurer的子类是被DelegatingWebMvcConfiguration类通过注入的方法读取到的,由于使用的是@Autowired的方式,所以要求我们自定义的类需要使用@Configuration或者@Bean、或者@Component等注解,保证这个类被Spring容器管理才可以生效。

DelegatingWebMvcConfiguration的addArgumentResolvers方法

@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
	this.configurers.addArgumentResolvers(argumentResolvers);
}

DelegatingWebMvcConfiguration中的configurers 成员变量,是通过@Autowired注解持有了所有自定义的WebMvcConfigurer的子类。

/**
 * A subclass of {@code WebMvcConfigurationSupport} that detects and delegates
 * to all beans of type {@link WebMvcConfigurer} allowing them to customize the
 * configuration provided by {@code WebMvcConfigurationSupport}. This is the
 * class actually imported by {@link EnableWebMvc @EnableWebMvc}.
 *
 * @author Rossen Stoyanchev
 * @since 3.1
 */
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	//这个成员变量是通过下面的@Autowired注解持有了所有自定义的WebMvcConfigurer的子类。
	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    //关键逻辑:此处通过@Autowire注解实现了所有的自定义WebMvcConfigurer子类的注入,
	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}

现在我们找到了Spring是如何读取自定义的WebMvcConfigurer 的子类的,那么Spring是在哪个地方使用的呢?
继续查看DelegatingWebMvcConfiguration 的addArgumentResolvers方法的调用链,我们发现是被WebMvcConfigurationSupport的addArgumentResolvers方法调用的,这个方法又被类内部的addDefaultHandlerExceptionResolvers方法和requestMappingHandlerAdapter方法调用,这个两个方法分别是用来初始化SpringBoot的默认异常处理解析器ExceptionHandlerExceptionResolver和默认的请求映射处理适配器RequestMappingHandlerAdapter,分别打开两个方法,我们发现我们自定义的ArgumentResolvers被分别设置到了ExceptionHandlerExceptionResolver类和RequestMappingHandlerAdapter类中。

WebMvcConfigurationSupport的addDefaultHandlerExceptionResolvers方法

protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
	ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
	exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager());
	exceptionHandlerResolver.setMessageConverters(getMessageConverters());
	//设置自定义的参数解析器(通过重写WebMvcConfigurer的addArgumentResolvers方法)
	exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
	exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers())

WebMvcConfigurationSupport的requestMappingHandlerAdapter方法

/**
 * 返回一个RequestMappingHandlerAdapter,用来处理Controller中使用了注解@RequestMapping的方法对应的请求
 * Returns a {@link RequestMappingHandlerAdapter} for processing requests
 * through annotated controller methods. Consider overriding one of these
 * other more fine-grained methods:
 * <ul>
 * addArgumentResolvers方法用来新增自定义的参数解析器
 * <li>{@link #addArgumentResolvers} for adding custom argument resolvers.
 * addReturnValueHandlers方法用来新增自定义的返回值处理器
 * <li>{@link #addReturnValueHandlers} for adding custom return value handlers.
 * configureMessageConverters方法用来新增自定义的消息转换器
 * <li>{@link #configureMessageConverters} for adding custom message converters.
 * </ul>
 */
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    //创建一个默认配置的RequestMappingHandlerAdapter 
	RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
	adapter.setContentNegotiationManager(mvcContentNegotiationManager());
	adapter.setMessageConverters(getMessageConverters());
	adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
	//设置自定义的参数解析器(通过重写WebMvcConfigurer的addArgumentResolvers方法)
	adapter.setCustomArgumentResolvers(getArgumentResolvers());
	adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

至此,我们找到了WebMvcConfigurer子类重写的方法是如何被SpringBoot读取并且使用的,通过这个方式,可以分别找到WebMvcConfigurer类其它的方法的读取和使用的逻辑。

后续Spring会在使用RequestMappingHandlerAdapter处理http请求或者使用ExceptionHandlerExceptionResolver 进行异常处理的时候,使用到我们自定义的ArgumentResolvers来进行参数的解析,如果对后续步骤感兴趣的话,可以去查看这两个类的源码。

通过查看方法调用链来定位和梳理逻辑是阅读源码的一种方式,可以帮助我们快速查找和分析源码,如果有更好的方式,欢迎评论或私信交流。

另外需要注意的是:
1、实现了WebMvcConfigruer的自定义类,一定要加上@Configuration或者@Component注解,保证这个类被Spring容器管理才可以生效。
2、定制SpringMVC的配置要通过实现WebMvcConfigurer接口的方式,而不能继承WebMvcConfigruerSupport,因为一旦继承了WebMvcConfigruerSupport,会导致MVC的自动配置类WebMvcAutoConfiguration失效,产生不必要的问题。

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
//如果容器中存在WebMvcConfigurationSupport类型的Bean,则WebMvcAutoConfiguration类不生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

可以看到,一旦容器中存在WebMvcConfigurationSupport类型的Bean,WebMvcAutoConfiguration类就不会生效。

本文为个人学习整理,如有描述错误或者对相关内容感兴趣,欢迎评论或私信交流,一起讨论、共同进步。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值