springboot2.7.6集成swagger3.0踩坑实录

一、背景

最近项目要升级项目框架,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. 

参考:

SpringBoot 最新教程:SpringBoot 2.7 整合 Swagger3.0 项目搭建-2022年最新图文版本_springboot webmvcendpointhandlermapping_架构师小冯的博客-CSDN博客

 解决“Failed to start bean ‘documentationPluginsBootstrapper‘; nested exception is java.lang.NullPoint”_chenxianchong的博客-CSDN博客

 解决 高版本SpringBoot整合Swagger 启动报错Failed to start bean ‘documentationPluginsBootstrapper‘ 问题_摸鱼佬的博客-CSDN博客

 

使用Knife4j 2.0.6及以上的版本报错无@EnableSwagger2注解-CSDN博客

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值