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接口方式不会有任何问,推荐使用这种开发方式