问题背景
项目使用了Swagger自动生成可视化文档,版本为2.9.2。正常情况下SpringBoot整合Swagger后默认就能访问文档页面。现在在项目中尝试配置SpringMvc拦截器,发现Swagger页面无法访问。
问题复现
在SpringBoot中引入Mvc配置,加入拦截器。有两种形式:
1、@Configuration配置类继承WebMvcConfigurationSupport,该类是Mvc配置的主要类。如:
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Autowired
private StatisticsInterceptor statisticsInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(statisticsInterceptor).addPathPatterns("/**");
}
}
2、@Configuration配置类上加入@EnableWebMvc注解,并实现WebMvcConfigurer,WebMvcConfigurer提供了部分Mvc配置。如
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Autowired
private StatisticsInterceptor statisticsInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(statisticsInterceptor).addPathPatterns("/**");
}
}
加入上述配置后,拦截器生效,但是Swagger页面报404。
原因分析
WebMvcAutoConfiguration自动配置
首先看看没有手动配置SpringMvc之前,SpringBoot自动做了什么?定位到WebMvcAutoConfiguration,代码片段如下:
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //注意这里的条件
... //省略无关注解
public class WebMvcAutoConfiguration {
// Defined as a nested config to ensure WebMvcConfigurer is not read when not
// on the classpath
@Import(EnableWebMvcConfiguration.class)
... //省略无关注解
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
@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/**")) {
// 添加了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)) {
// 添加了 /** 到 "/META-INF/resources/","/resources/", "/static/", "/public/" 的路径映射
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
}
/**
* Configuration equivalent to {@code @EnableWebMvc}.
*/
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
...
}
}
关键类是WebMvcAutoConfigurationAdapter和EnableWebMvcConfiguration。
可以看到,SpringBoot在不存在WebMvcConfigurationSupport配置类时,会引入WebMvcAutoConfigurationAdapter,这个类是
WebMvcConfigurer的实现类,给Mvc加入了一些默认配置,其中就包括加入了两个资源处理器。
等同于
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/")
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/")
此外WebMvcAutoConfigurationAdapter类上@Import(EnableWebMvcConfiguration.class)引入了下面的EnableWebMvcConfiguration类,这个类继承了DelegatingWebMvcConfiguration,而DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport。
这四个类的关系如下所示:
DelegatingWebMvcConfiguration内部有WebMvcConfigurer的组合类WebMvcConfigurerComposite,DelegatingWebMvcConfiguration中对Mvc配置的处理,实际上是委托多个WebMvcConfigurer来完成。
至此,我们可以看出,WebMvcAutoConfiguration自动配置的原理和@EnableWebMvc+WebMvcConfigurer效果是一样的,@EnableWebMvc注解引入的是DelegatingWebMvcConfiguration类。
原因说明
了解了Mvc自动配置的原理后,本文开头出现的问题原因就很明朗了。因为我们手动引入了WebMvcConfigurationSupport对象,因此自动配置不会生效。自动配置时,系统默认设置了两个资源处理器处理静态资源;而上面的手动配置只加入了拦截器,并没有配置资源处理器。
在springfox-swagger-ui-2.9.2.jar中,swagger-ui.html放在META-INF/resources下面,没有配置资源处理器所以无法访问。
解决方法
以@EnableWebMvc+WebMvcConfigurer的方式配置为例,加入资源映射处理器,和自动配置保持一致即可:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Autowired
private StatisticsInterceptor statisticsInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(statisticsInterceptor).addPathPatterns("/**");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//swagger-2.9.2静态资源不是在webjars目录下,实际验证只需要下面的配置即可
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/");
}
}