一、背景
最近项目要升级项目框架,springboot从2.1.8.RELEASE升级到2.7.6,springcloud从Finchley.SR2升级到2021.0.5
升级了框架,启动报错,发现之前集成的swagger也要进行升级,就在这里记录一下踩的坑和解决方法。本文所有业务代码都是demo。
二、集成步骤
1、pom.xml引入
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
这里排除guava重新引入是因为低版本有漏洞。
2、新增配置类
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${swagger.base-package:}")
private String base_package;
@Value("${swagger.title:}")
private String title;
@Value("${swagger.description:}")
private String description;
@Value("${swagger.version:}")
private String version;
@Value("${swagger.terms-of-service-url:}")
private String terms_of_service_url;
@Value("${swagger.contact.name:}")
private String contact_name;
@Value("${swagger.contact.url:}")
private String contact_url;
@Value("${swagger.contact.email:}")
private String contact_email;
@Bean
public Docket api() {
List<Parameter> parameterBuilders = new ArrayList<>();
ParameterBuilder userId = new ParameterBuilder();
userId.name("userId").defaultValue("").description("用户标识").modelRef(new ModelRef("long")).parameterType("header").required(false).build();
parameterBuilders.add(userId.build());
ParameterBuilder groupId = new ParameterBuilder();
groupId.name("groupId").defaultValue("").description("用户组标识").modelRef(new ModelRef("long")).parameterType("header").required(false).build();
parameterBuilders.add(groupId.build());
ParameterBuilder userName = new ParameterBuilder();
userName.name("userName").defaultValue("").description("用户名称").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
parameterBuilders.add(userName.build());
Predicate<RequestHandler> apis = RequestHandlerSelectors.any();
if (StringUtils.isNotBlank(base_package)) {
apis = RequestHandlerSelectors.basePackage(base_package);
}
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(apis)
.paths(PathSelectors.any())
.build().globalOperationParameters(parameterBuilders);
}
private ApiInfo apiInfo() {
ApiInfoBuilder apiInfoBuilder = new ApiInfoBuilder();
if (StringUtils.isNotBlank(title)) {
apiInfoBuilder.title(title);
}
if (StringUtils.isNotBlank(description)) {
apiInfoBuilder.description(description);
}
if (StringUtils.isNotBlank(version)) {
apiInfoBuilder.version(version);
}
if (StringUtils.isNotBlank(terms_of_service_url)) {
apiInfoBuilder.termsOfServiceUrl(terms_of_service_url);
}
if (StringUtils.isNotBlank(contact_name) || StringUtils.isNotBlank(contact_url) || StringUtils.isNotBlank(contact_email)) {
apiInfoBuilder.contact(new Contact(contact_name, contact_url, contact_email));
}
return apiInfoBuilder.build();
}
其中输入的属性在yml配置文件中增加:
swagger:
base-package: com.xxx.controller # 改为自己的包路径
title: xxx平台_AAA服务
description: xxx平台_AAA服务接口
version: 3.0
terms-of-service-url:
contact:
name: zhangsan
email: zhangsan@qq.com
也可以直接在上面config类写死。
3、实体类使用注解参考
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "个人信息", description = "")
public class Person {
@ApiModelProperty(value = "姓名", required = true)
private String name;
@ApiModelProperty(value = "年龄", required = false)
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
4、controller类使用注解参考
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.aspire.gmds.entity.Person;
import com.aspire.gmds.entity.ResultData;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
@Api(tags = "个人信息接口")
@RestController
@RequestMapping("/person")
public class PersonController {
@ApiOperation(value = "查询列表", notes = "")
@RequestMapping(value = "/list", method = {RequestMethod.POST})
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "姓名", required = false, paramType = "query", dataType = "string")
})
public ResultData<List<Person>> list(String name) {
List<Person> list = new ArrayList<Person>();
Person person = new Person();
person.setName("张三");
person.setAge(23);
list.add(person);
return ResultData.success(200, "ok", list);
}
}
其中使用到的ResultData代码如下
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.http.HttpStatus;
@ApiModel(value = "返回结果信息", description = "")
public class ResultData<T> {
@ApiModelProperty(value = "状态码", required = true, example = "200")
private Integer status;
@ApiModelProperty(value = "状态说明", required = false, example = "ok")
private String msg;
@ApiModelProperty(value = "结果数据", required = false)
private T data;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static <T> ResultData<T> success(T data) {
ResultData resultData = new ResultData();
resultData.setStatus(HttpStatus.OK.value());
resultData.setMsg(HttpStatus.OK.name());
resultData.setData(data);
return resultData;
}
public static ResultData success() {
ResultData resultData = new ResultData();
resultData.setStatus(HttpStatus.OK.value());
resultData.setMsg(HttpStatus.OK.name());
return resultData;
}
public static <T> ResultData<T> success(Integer status, String msg, T data) {
ResultData resultData = new ResultData();
resultData.setStatus(status);
resultData.setMsg(msg);
resultData.setData(data);
return resultData;
}
public static ResultData fail(Integer status, String msg) {
ResultData resultData = new ResultData();
resultData.setStatus(status);
resultData.setMsg(msg);
return resultData;
}
三、启动验证
1、不出意外的话,会报错如下:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-04-12 17:35:09.953 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
Failed to start bean 'documentationPluginsBootstrapper' 百度找这个报错,五花八门的解决方法都有,能找到一个符合自己项目的,要花挺多时间。
我找到合适我的解决方法,增加config类:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;
@Configuration
public class BeanPostProcessorConfig {
@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);
}
}
};
}
}
再次启动,没报错了,访问swagger页面:http://localhost:8480/doc.html
看着好像没问题了,不过我们前面controller加了注解,没在页面出来
再增加一个config类:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurationSupport {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html", "doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
}
再次访问 swagger页面:http://localhost:8480/doc.html
可以看到接口能正常出来,点击调试,也没问题
over.
参考: