背景
原先的项目一部分在使用 SpringBoot 2.4,但 2.4 的版本中对于线程池没有默认的监控,需要自己去实现。但是在 2.6 版本有默认实现监控,所以想将项目升级到 2.6。
升级&问题
SpringBoot 和 SpringCloud 版本的对应关系,参考:https://start.spring.io/actuator/info
修改好 pom 后,刷新 maven 没有报错,启动出现了异常,核心错误如下:
Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
网上检索到的原因和解决方案,如下:
原因:这是因为 Springfox 使用的路径匹配是基于 AntPathMatcher 的,而 Spring Boot 2.6.X 使用的是 PathPatternMatcher。
解决:
- 在 application.properties 里配置:spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER。
- 添加如下代码
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
具体可参考:https://github.com/springfox/springfox/issues/3462
适应新版本的解决方案
上述的方案,试过没有什么问题,但是感觉侵入性太强,修改了 spring 升级的内容,仔细看了 issue 的回复
spring 官方其实已经推出了新的 springdoc 来作为 springfox 的替代。
按照文档进行迁移:https://springdoc.org/migrating-from-springfox.html ,其实就两步:
- 删除 swagger 的 jar 包和相关配置
- 引入 jar 包
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
- 增加 springdoc 的配置,如下是我的 demo 案例
@Configuration
public class SpringdocConfiguration {
@Bean
public GroupedOpenApi publicApi() {
String[] contextHeaderNames = new String[]{"M-Token", "M-UserId"};
return GroupedOpenApi.builder()
.group("mine-demo")
.packagesToScan("io.github.demo")
.pathsToMatch("/**")
.addOperationCustomizer((operation, handlerMethod) -> {
for (String contextHeaderName : contextHeaderNames) {
Parameter missingParam = new Parameter()
.in(ParameterIn.HEADER.toString())
.schema(new StringSchema())
.name(contextHeaderName);
operation.addParametersItem(missingParam);
}
return operation;
})
.build();
}
@Bean
public OpenAPI apiInfo() {
return new OpenAPI().info(new Info().title("mine测试案例").version("1.0.0"));
}
}
- 刷新 maven,启动,访问/swagger-ui.html 即可跳转到/swagger-ui/index.html
使用 knife4j 的 ui
- 引入 jar 包,这里注意下版本,具体看官方文档:https://doc.xiaominfo.com/,我这边用的是 spring boot 2.6,所以对应使用
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
这里需要注意 knife4j 中已经依赖了 springdoc-openapi-ui,如果和前面引入的版本不一致,则需要把前面引入的 jar 移除掉,不然会版本冲突。
- 在 application.yml 中增加配置,开启 knife4j 的 ui
knife4j:
enable: true
- 刷新 maven 启动,访问/doc.html