SpringMVC自动配置类避坑及源码详解


在SpringBoot中SpringMVC默认自动配置了在spring-boot-autoconfigure.jar/META-INF/spring.factories中看到如下:

在这里插入图片描述
本文主要介绍mvc配置类org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,这个类中定义了默认的配置,例如静态资源文件的访问。

WebMvcAutoConfiguration类介绍

这个类的定义源码如下:

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration { 
	................
}

这个类的实例化必须先实例化 Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class 这3个类。这个配置类生效的前提是@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) ,容器中没有WebMvcConfigurationSupport类或者子类进行了注册。

重点关注WebMvcConfigurer.class这个接口。实现这个接口可以自定义组件覆盖SpringMVC的组件的默认配置。
WebMvcRegistrations接口现类,可以注册自定义的处理器映射器,处理器适配器和异常处理器解析器覆盖默认的组件

实现自定义配置的方式

方式一:实现WebMvcConfigurer接口和WebMvcRegistrations接口

实现WebMvcConfigurer接口可以修改SpringMVC一些组件的配置
实现WebMvcRegistrations接口可以使用自定义的三大组件

实现原理:
正常情况下spring会导入WebMvcAutoConfiguration类为SpringMVC的自动装配类,这个类中有两个内部类如下:

@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter
		implements WebMvcConfigurer, ResourceLoaderAware {...}


@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
	private final WebMvcRegistrations mvcRegistrations;
	public EnableWebMvcConfiguration(
			ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
			ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
			ListableBeanFactory beanFactory) {
		this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
		this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
		this.beanFactory = beanFactory;
	}

	@Override
	protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
		if (this.mvcRegistrations != null
				&& this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {
			return this.mvcRegistrations.getRequestMappingHandlerAdapter();
		}
		return super.createRequestMappingHandlerAdapter();
	}

	@Override
	protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
		if (this.mvcRegistrations != null && this.mvcRegistrations
				.getExceptionHandlerExceptionResolver() != null) {
			return this.mvcRegistrations.getExceptionHandlerExceptionResolver();
		}
		return super.createExceptionHandlerExceptionResolver();
	}

	@Override
	protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
		if (this.mvcRegistrations != null
				&& this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
			return this.mvcRegistrations.getRequestMappingHandlerMapping();
		}
		return super.createRequestMappingHandlerMapping();
	}
}

先看内部类WebMvcAutoConfigurationAdapter的类注解上导入了 spring.mvc的配置类WebMvcProperties.class和资源静态资源文件配置类ResourceProperties.class ,并且实现了WebMvcConfigurer接口对资源处理器,国际化的组件进行了覆盖做默认配置,所以当WebMvcAutoConfiguration配置类没有创建就会导致springboot默认的静态资源存放位置无法访问。这个内部类中到导入了另一个内部类EnableWebMvcConfiguration。

EnableWebMvcConfiguration这个类继承关系图如下:
在这里插入图片描述
首先这个类是继承了DelegatingWebMvcConfiguration 类,从而间接继承了WebMvcConfigurationSupport类
先看这个类的构造方法:

private final WebMvcRegistrations mvcRegistrations;
public EnableWebMvcConfiguration(
				ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
				ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
				ListableBeanFactory beanFactory) {
			this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
			this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
			this.beanFactory = beanFactory;
		}

这个构造方法中注入了ObjectProvider mvcRegistrationsProvider参数,也就是WebMvcRegistrations接口实现类。
再看几个成员方法:

//创建处理器适配器
@Override
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
	if (this.mvcRegistrations != null
			&& this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {
		return this.mvcRegistrations.getRequestMappingHandlerAdapter();
	}
	return super.createRequestMappingHandlerAdapter();
}

//创建处理器映射器
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
	if (this.mvcRegistrations != null
			&& this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
		return this.mvcRegistrations.getRequestMappingHandlerMapping();
	}
	return super.createRequestMappingHandlerMapping();
}

//创建异常解析器
@Override
protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
	if (this.mvcRegistrations != null && this.mvcRegistrations
			.getExceptionHandlerExceptionResolver() != null) {
		return this.mvcRegistrations.getExceptionHandlerExceptionResolver();
	}
	return super.createExceptionHandlerExceptionResolver();
}

上面三个组件的创建如果注入的WebMvcRegistrations接口实现类不为空,则从实现类中获取三大自定义组件去覆盖系统默认的。
所以利用WebMvcRegistrations接口实现自定义的springMVC三大组件只需要实现该接口,然后交个spring管理即可。

再看父类DelegatingWebMvcConfiguration有如下成员变量和方法

private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
	if (!CollectionUtils.isEmpty(configurers)) {
		this.configurers.addWebMvcConfigurers(configurers);
	}
}

这里注入了一个WebMvcConfigurer接口实现类,所以当我们自定义spingMVC配置组件的时候实现该接口会被这个类持有。当调用到接口方法的时候就会委托给成员变量configurers调用到实际的接口实现方法。

方式二:实现WebMvcConfigurer接口和使用@EnableWebMvc注解

开发实现如下:

@EnableWebMvc
@Component
public class MyWebMvcConfiguration implements WebMvcConfigurer {

这种开发方式关键是看@EnableWebMvc注解,这个注解如下

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

这个注解导入了一个类DelegatingWebMvcConfiguration,之前说过这个类是WebMvcConfigurationSupport类的子类,而由于我们主动导入并注入到容器中,所以会先于spring自定义的组件加载,所以等到加载springboot的springmvc配置类WebMvcAutoConfiguration 的时候,@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)条件已经不满足,导致上面的springboot自定义配置类实现,而是使用WebMvcConfigurationSupport类中定义的配置为默认配置,也就会导致默认的静态资源处理方式失效,而找不到默认的静态资源文件。

另外,WebMvcAutoConfiguration 配置类失效导致EnableWebMvcConfiguration配置类也无法加载,而时间实现WebMvcRegistrations接口交个spring管理来进行扩展的方式也就失效了。

非要使用这种方式,且要扩展WebMvcRegistrations接口重写组件。可以考虑继承WebMvcConfigurationSupport类,重写
createRequestMappingHandlerMapping()
createRequestMappingHandlerAdapter() createExceptionHandlerExceptionResolver()
这三个方法,然后将类交个spring管理接口达到以上接口的效果。

总结

实现WebMvcConfigurer接口 + @EnableWebMvc 注解的方式扩展,会导致WebMvcAutoConfiguration不加载,所以默认配置丢失导致静态资源等无法访问

实现WebMvcConfigurer接口 和 WebMvcRegistrations接口方式不会有任何问,推荐使用这种开发方式

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值