springboot中使用@EnableWebMvc时导致静态资
代码
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptors()).addPathPatterns("/**").excludePathPatterns("/admin/**");
}
}
如果把@EnableWebMvc注解去掉的话就可以访问静态资源。
首先看下springboot默认是如何访问静态资源的:
以下是实现了WebMvcConfigurer接口的类
WebMvcConfigurerAdapter
WebConfig //自定义的
WebMvcConfigurerComposite
WebMvcAutoConfigurationAdapter in WebMvcAutoConfiguration
Neo4jWebMvcConfiguration in Neo4jWebConfiguration in Neo4jDataAutoConfiguration
JpaWebMvcConfiguration in JpaWebConfiguration in JpaBaseConfiguration
其中WebMvcConfigurerComposite 和 WebMvcAutoConfigurationAdapte中重写了addResourceHandlers方法
WebMvcConfigurerComposite :
private final List<WebMvcConfigurer> delegates = new ArrayList<>();
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.delegates.addAll(configurers);
}
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//遍历所有的WebMvcConfigurer(自定义的和下面的默认的一个WebMvcAutoConfigurationAdapter)
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addResourceHandlers(registry);
}
}
WebMvcAutoConfigurationAdapter
@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 {
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter
implements WebMvcConfigurer, ResourceLoaderAware {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache()
.getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(
this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
从上面的代码可以看出,WebMvcConfigurerComposite 中会遍历所有的WebMvcConfigurer,所以即使在自定义的配置类中重写了addResourceHandlers方法,在遍历的时候会被默认的覆盖,因为在WebMvcAutoConfigurationAdapter也有addResourceHandlers方法
webmvcconfig.png
但是加了@EnableWebMvc之后,为什么就不行了呢,先看下源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
这里有个@Import(DelegatingWebMvcConfiguration.class),它是一个WebMvcConfigurationSupport ,并重写了addResourceHandlers方法,里面调用的还是之前WebMvcConfigurerComposite中的addResourceHandlers方法。
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
}
那么此时,再调试一下。会发现此时只有自定义的一个配置类了,原本的WebMvcAutoConfiguration不在了
webmvcconfig2.png
此时再观察一下WebMvcAutoConfiguration中有一个注解:
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
总结:
用户配置了@EnableWebMvc
Spring扫描所有的注解,再从注解上扫描到@Import,把这些@Import引入的bean信息都缓存起来 在扫描到@EnableWebMvc时,通过@Import加入了 DelegatingWebMvcConfiguration,也就是WebMvcConfigurationSupport
spring再处理@Conditional相关的注解,判断发现已有WebMvcConfigurationSupport,就跳过了spring boot的WebMvcAutoConfiguration,所以默认的也就失效了。