SpringBoot整合Swagger3.0使用及报错解决大全
创始人
2024-06-03 08:09:01
0

前言

Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。

在这里插入图片描述

StepDescription
1引入Maven依赖
2在Spring Boot中启用Swagger
3创建SwaggerConfig类
4创建Docket Bean
5提供API信息
6配置Swagger UI
7应用Swagger

项目背景

在这里插入图片描述

版本

SpringBoot 2.7.*
springfox 3.0

Maven依赖


io.springfoxspringfox-swagger23.0.0


io.springfoxspringfox-boot-starter3.0.0

@Configuration
@EnableSwagger2
@EnableAutoConfiguration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration
{/*** 默认的排除路径,排除Spring Boot默认的错误处理路径和端点*/private static final List DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");private static final String BASE_PATH = "/**";@Bean@ConditionalOnMissingBeanpublic SwaggerProperties swaggerProperties(){return new SwaggerProperties();}@Beanpublic Docket api(SwaggerProperties swaggerProperties){// base-path处理if (swaggerProperties.getBasePath().isEmpty()){swaggerProperties.getBasePath().add(BASE_PATH);}// noinspection uncheckedList> basePath = new ArrayList>();swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));// exclude-path处理if (swaggerProperties.getExcludePath().isEmpty()){swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);}List> excludePath = new ArrayList<>();swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2).host(swaggerProperties.getHost()).apiInfo(apiInfo(swaggerProperties)).select().apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()));swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p)));swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));return builder.build().securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping("/");}/*** 安全模式,这里指定token通过Authorization头请求头传递*/private List securitySchemes(){List apiKeyList = new ArrayList();apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));return apiKeyList;}/*** 安全上下文*/private List securityContexts(){List securityContexts = new ArrayList<>();securityContexts.add(SecurityContext.builder().securityReferences(defaultAuth()).operationSelector(o -> o.requestMappingPattern().matches("/.*")).build());return securityContexts;}/*** 默认的全局鉴权策略** @return*/private List defaultAuth(){AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];authorizationScopes[0] = authorizationScope;List securityReferences = new ArrayList<>();securityReferences.add(new SecurityReference("Authorization", authorizationScopes));return securityReferences;}private ApiInfo apiInfo(SwaggerProperties swaggerProperties){return new ApiInfoBuilder().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription()).license(swaggerProperties.getLicense()).licenseUrl(swaggerProperties.getLicenseUrl()).termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()).contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())).version(swaggerProperties.getVersion()).build();}
}
@Component
@ConfigurationProperties("swagger")
public class SwaggerProperties
{/*** 是否开启swagger*/private Boolean enabled;/*** swagger会解析的包路径**/private String basePackage = "";/*** swagger会解析的url规则**/private List basePath = new ArrayList<>();/*** 在basePath基础上需要排除的url规则**/private List excludePath = new ArrayList<>();/*** 标题**/private String title = "";/*** 描述**/private String description = "";/*** 版本**/private String version = "";/*** 许可证**/private String license = "";/*** 许可证URL**/private String licenseUrl = "";/*** 服务条款URL**/private String termsOfServiceUrl = "";/*** host信息**/private String host = "";/*** 联系人信息*/private Contact contact = new Contact();/*** 全局统一鉴权配置**/private Authorization authorization = new Authorization();public Boolean getEnabled(){return enabled;}public void setEnabled(Boolean enabled){this.enabled = enabled;}public String getBasePackage(){return basePackage;}public void setBasePackage(String basePackage){this.basePackage = basePackage;}public List getBasePath(){return basePath;}public void setBasePath(List basePath){this.basePath = basePath;}public List getExcludePath(){return excludePath;}public void setExcludePath(List excludePath){this.excludePath = excludePath;}public String getTitle(){return title;}public void setTitle(String title){this.title = title;}public String getDescription(){return description;}public void setDescription(String description){this.description = description;}public String getVersion(){return version;}public void setVersion(String version){this.version = version;}public String getLicense(){return license;}public void setLicense(String license){this.license = license;}public String getLicenseUrl(){return licenseUrl;}public void setLicenseUrl(String licenseUrl){this.licenseUrl = licenseUrl;}public String getTermsOfServiceUrl(){return termsOfServiceUrl;}public void setTermsOfServiceUrl(String termsOfServiceUrl){this.termsOfServiceUrl = termsOfServiceUrl;}public String getHost(){return host;}public void setHost(String host){this.host = host;}public Contact getContact(){return contact;}public void setContact(Contact contact){this.contact = contact;}public Authorization getAuthorization(){return authorization;}public void setAuthorization(Authorization authorization){this.authorization = authorization;}public static class Contact{/*** 联系人**/private String name = "";/*** 联系人url**/private String url = "";/*** 联系人email**/private String email = "";public String getName(){return name;}public void setName(String name){this.name = name;}public String getUrl(){return url;}public void setUrl(String url){this.url = url;}public String getEmail(){return email;}public void setEmail(String email){this.email = email;}}public static class Authorization{/*** 鉴权策略ID,需要和SecurityReferences ID保持一致*/private String name = "";/*** 需要开启鉴权URL的正则*/private String authRegex = "^.*$";/*** 鉴权作用域列表*/private List authorizationScopeList = new ArrayList<>();private List tokenUrlList = new ArrayList<>();public String getName(){return name;}public void setName(String name){this.name = name;}public String getAuthRegex(){return authRegex;}public void setAuthRegex(String authRegex){this.authRegex = authRegex;}public List getAuthorizationScopeList(){return authorizationScopeList;}public void setAuthorizationScopeList(List authorizationScopeList){this.authorizationScopeList = authorizationScopeList;}public List getTokenUrlList(){return tokenUrlList;}public void setTokenUrlList(List tokenUrlList){this.tokenUrlList = tokenUrlList;}}public static class AuthorizationScope{/*** 作用域名称*/private String scope = "";/*** 作用域描述*/private String description = "";public String getScope(){return scope;}public void setScope(String scope){this.scope = scope;}public String getDescription(){return description;}public void setDescription(String description){this.description = description;}}
/*** swagger 资源映射路径* */
@Configuration
public class SwaggerWebConfiguration implements WebMvcConfigurer
{@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry){/** swagger-ui 地址 */registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");}
}

报错一:: Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerException

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerExceptionat 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)at java.lang.Iterable.forEach(Iterable.java:75)at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)at *Application.main(Application.java:22)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.NullPointerException: nullat springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56)at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113)at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89)at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)at java.util.TimSort.sort(TimSort.java:234)at java.util.Arrays.sort(Arrays.java:1512)at java.util.ArrayList.sort(ArrayList.java:1454)at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:387)at java.util.stream.Sink$ChainedReference.end(Sink.java:258)at java.util.stream.Sink$ChainedReference.end(Sink.java:258)at java.util.stream.Sink$ChainedReference.end(Sink.java:258)at java.util.stream.Sink$ChainedReference.end(Sink.java:258)at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81)at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107)at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91)at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82)at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100)at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)... 19 common frames omitted

从报错顺序跟踪源码查看执行步骤

step操作
1SpringBoot 启动同时Bean初始化完毕
2初始化DocumentationPluginsBootstrapper同时将RequestHandlerProvider通过构造器驻入
3调用DocumentationPluginsBootstrapper start 方法加载Docket插件
4解析RequestHandlerProvider并将数据存到DocumentationCache中
5请求Swagger2Controller 的v2/api-docs接口,通过groupName从DocumentationCache中将数据取出,在将数据统一封装到Swagger类中,序列化成json返回给

报错二:No operations defined in spec!

在这里插入图片描述

解决

SpringBoot 2.6.0开始,请求路径与SpringMVC处理映射匹配的默认策略已从AntPathMatcher更改为PathPatternParser。可以通过设置spring.mvc.pathmatch.matching-strategy为ant-path-matcher来改变。

除了basePackage包路径配错以外。以下方案可解决以上问题。

/*** swagger 在 springboot 2.6.x 不兼容问题的处理**/
@Component
public class SwaggerBeanPostProcessor implements BeanPostProcessor
{@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider){customizeSpringfoxHandlerMappings(getHandlerMappings(bean));}return bean;}private  void customizeSpringfoxHandlerMappings(List mappings){List copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());mappings.clear();mappings.addAll(copy);}@SuppressWarnings("unchecked")private List getHandlerMappings(Object bean){try{Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");field.setAccessible(true);return (List) field.get(bean);}catch (IllegalArgumentException | IllegalAccessException e){throw new IllegalStateException(e);}}
}

配置:

spring:mvc:pathmatch:matching-strategy: ant_path_matcher

在这里插入图片描述

心如欲壑,后土难填。

相关内容

热门资讯

电视安卓系统哪个品牌好,哪家品... 你有没有想过,家里的电视是不是该升级换代了呢?现在市面上电视品牌琳琅满目,各种操作系统也是让人眼花缭...
安卓会员管理系统怎么用,提升服... 你有没有想过,手机里那些你爱不释手的APP,背后其实有个强大的会员管理系统在默默支持呢?没错,就是那...
安卓系统软件使用技巧,解锁软件... 你有没有发现,用安卓手机的时候,总有一些小技巧能让你玩得更溜?别小看了这些小细节,它们可是能让你的手...
安卓系统提示音替换 你知道吗?手机里那个时不时响起的提示音,有时候真的能让人心情大好,有时候又让人抓狂不已。今天,就让我...
安卓开机不了系统更新 手机突然开不了机,系统更新还卡在那里,这可真是让人头疼的问题啊!你是不是也遇到了这种情况?别急,今天...
安卓系统中微信视频,安卓系统下... 你有没有发现,现在用手机聊天,视频通话简直成了标配!尤其是咱们安卓系统的小伙伴们,微信视频功能更是用...
安卓系统是服务器,服务器端的智... 你知道吗?在科技的世界里,安卓系统可是个超级明星呢!它不仅仅是个手机操作系统,竟然还能成为服务器的得...
pc电脑安卓系统下载软件,轻松... 你有没有想过,你的PC电脑上安装了安卓系统,是不是瞬间觉得世界都大不一样了呢?没错,就是那种“一机在...
电影院购票系统安卓,便捷观影新... 你有没有想过,在繁忙的生活中,一部好电影就像是一剂强心针,能瞬间让你放松心情?而我今天要和你分享的,...
安卓系统可以写程序? 你有没有想过,安卓系统竟然也能写程序呢?没错,你没听错!这个我们日常使用的智能手机操作系统,竟然有着...
安卓系统架构书籍推荐,权威书籍... 你有没有想过,想要深入了解安卓系统架构,却不知道从何下手?别急,今天我就要给你推荐几本超级实用的书籍...
安卓系统看到的炸弹,技术解析与... 安卓系统看到的炸弹——揭秘手机中的隐形威胁在数字化时代,智能手机已经成为我们生活中不可或缺的一部分。...
鸿蒙系统有安卓文件,畅享多平台... 你知道吗?最近在科技圈里,有个大新闻可是闹得沸沸扬扬的,那就是鸿蒙系统竟然有了安卓文件!是不是觉得有...
宝马安卓车机系统切换,驾驭未来... 你有没有发现,现在的汽车越来越智能了?尤其是那些豪华品牌,比如宝马,它们的内饰里那个大屏幕,简直就像...
p30退回安卓系统 你有没有听说最近P30的用户们都在忙活一件大事?没错,就是他们的手机要退回安卓系统啦!这可不是一个简...
oppoa57安卓原生系统,原... 你有没有发现,最近OPPO A57这款手机在安卓原生系统上的表现真是让人眼前一亮呢?今天,就让我带你...
安卓系统输入法联想,安卓系统输... 你有没有发现,手机上的输入法真的是个神奇的小助手呢?尤其是安卓系统的输入法,简直就是智能生活的点睛之...
怎么进入安卓刷机系统,安卓刷机... 亲爱的手机控们,你是否曾对安卓手机的刷机系统充满好奇?想要解锁手机潜能,体验全新的系统魅力?别急,今...
安卓系统程序有病毒 你知道吗?在这个数字化时代,手机已经成了我们生活中不可或缺的好伙伴。但是,你知道吗?即使是安卓系统,...
奥迪中控安卓系统下载,畅享智能... 你有没有发现,现在汽车的中控系统越来越智能了?尤其是奥迪这种豪华品牌,他们的中控系统简直就是科技与艺...