0


SpringBoot整合Swagger3.0使用及报错解决大全

前言

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

在这里插入图片描述
StepDescription1引入Maven依赖2在Spring Boot中启用Swagger3创建SwaggerConfig类4创建Docket Bean5提供API信息6配置Swagger UI7应用Swagger

项目背景

在这里插入图片描述

版本

SpringBoot 2.7.*
springfox 3.0

Maven依赖

<!-- Swagger --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>3.0.0</version></dependency><!-- 或--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency>
@Configuration@EnableSwagger2@EnableAutoConfiguration@ConditionalOnProperty(name ="swagger.enabled", matchIfMissing =true)publicclassSwaggerAutoConfiguration{/**
     * 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
     */privatestaticfinalList<String> DEFAULT_EXCLUDE_PATH =Arrays.asList("/error","/actuator/**");privatestaticfinalString BASE_PATH ="/**";@Bean@ConditionalOnMissingBeanpublicSwaggerPropertiesswaggerProperties(){returnnewSwaggerProperties();}@BeanpublicDocketapi(SwaggerProperties swaggerProperties){// base-path处理if(swaggerProperties.getBasePath().isEmpty()){
            swaggerProperties.getBasePath().add(BASE_PATH);}// noinspection uncheckedList<Predicate<String>> basePath =newArrayList<Predicate<String>>();
        swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));// exclude-path处理if(swaggerProperties.getExcludePath().isEmpty()){
            swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);}List<Predicate<String>> excludePath =newArrayList<>();
        swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));ApiSelectorBuilder builder =newDocket(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头请求头传递
     */privateList<SecurityScheme>securitySchemes(){List<SecurityScheme> apiKeyList =newArrayList<SecurityScheme>();
        apiKeyList.add(newApiKey("Authorization","Authorization","header"));return apiKeyList;}/**
     * 安全上下文
     */privateList<SecurityContext>securityContexts(){List<SecurityContext> securityContexts =newArrayList<>();
        securityContexts.add(SecurityContext.builder().securityReferences(defaultAuth()).operationSelector(o -> o.requestMappingPattern().matches("/.*")).build());return securityContexts;}/**
     * 默认的全局鉴权策略
     *
     * @return
     */privateList<SecurityReference>defaultAuth(){AuthorizationScope authorizationScope =newAuthorizationScope("global","accessEverything");AuthorizationScope[] authorizationScopes =newAuthorizationScope[1];
        authorizationScopes[0]= authorizationScope;List<SecurityReference> securityReferences =newArrayList<>();
        securityReferences.add(newSecurityReference("Authorization", authorizationScopes));return securityReferences;}privateApiInfoapiInfo(SwaggerProperties swaggerProperties){returnnewApiInfoBuilder().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription()).license(swaggerProperties.getLicense()).licenseUrl(swaggerProperties.getLicenseUrl()).termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()).contact(newContact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())).version(swaggerProperties.getVersion()).build();}}
@Component@ConfigurationProperties("swagger")publicclassSwaggerProperties{/**
     * 是否开启swagger
     */privateBoolean enabled;/**
     * swagger会解析的包路径
     **/privateString basePackage ="";/**
     * swagger会解析的url规则
     **/privateList<String> basePath =newArrayList<>();/**
     * 在basePath基础上需要排除的url规则
     **/privateList<String> excludePath =newArrayList<>();/**
     * 标题
     **/privateString title ="";/**
     * 描述
     **/privateString description ="";/**
     * 版本
     **/privateString version ="";/**
     * 许可证
     **/privateString license ="";/**
     * 许可证URL
     **/privateString licenseUrl ="";/**
     * 服务条款URL
     **/privateString termsOfServiceUrl ="";/**
     * host信息
     **/privateString host ="";/**
     * 联系人信息
     */privateContact contact =newContact();/**
     * 全局统一鉴权配置
     **/privateAuthorization authorization =newAuthorization();publicBooleangetEnabled(){return enabled;}publicvoidsetEnabled(Boolean enabled){this.enabled = enabled;}publicStringgetBasePackage(){return basePackage;}publicvoidsetBasePackage(String basePackage){this.basePackage = basePackage;}publicList<String>getBasePath(){return basePath;}publicvoidsetBasePath(List<String> basePath){this.basePath = basePath;}publicList<String>getExcludePath(){return excludePath;}publicvoidsetExcludePath(List<String> excludePath){this.excludePath = excludePath;}publicStringgetTitle(){return title;}publicvoidsetTitle(String title){this.title = title;}publicStringgetDescription(){return description;}publicvoidsetDescription(String description){this.description = description;}publicStringgetVersion(){return version;}publicvoidsetVersion(String version){this.version = version;}publicStringgetLicense(){return license;}publicvoidsetLicense(String license){this.license = license;}publicStringgetLicenseUrl(){return licenseUrl;}publicvoidsetLicenseUrl(String licenseUrl){this.licenseUrl = licenseUrl;}publicStringgetTermsOfServiceUrl(){return termsOfServiceUrl;}publicvoidsetTermsOfServiceUrl(String termsOfServiceUrl){this.termsOfServiceUrl = termsOfServiceUrl;}publicStringgetHost(){return host;}publicvoidsetHost(String host){this.host = host;}publicContactgetContact(){return contact;}publicvoidsetContact(Contact contact){this.contact = contact;}publicAuthorizationgetAuthorization(){return authorization;}publicvoidsetAuthorization(Authorization authorization){this.authorization = authorization;}publicstaticclassContact{/**
         * 联系人
         **/privateString name ="";/**
         * 联系人url
         **/privateString url ="";/**
         * 联系人email
         **/privateString email ="";publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicStringgetUrl(){return url;}publicvoidsetUrl(String url){this.url = url;}publicStringgetEmail(){return email;}publicvoidsetEmail(String email){this.email = email;}}publicstaticclassAuthorization{/**
         * 鉴权策略ID,需要和SecurityReferences ID保持一致
         */privateString name ="";/**
         * 需要开启鉴权URL的正则
         */privateString authRegex ="^.*$";/**
         * 鉴权作用域列表
         */privateList<AuthorizationScope> authorizationScopeList =newArrayList<>();privateList<String> tokenUrlList =newArrayList<>();publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicStringgetAuthRegex(){return authRegex;}publicvoidsetAuthRegex(String authRegex){this.authRegex = authRegex;}publicList<AuthorizationScope>getAuthorizationScopeList(){return authorizationScopeList;}publicvoidsetAuthorizationScopeList(List<AuthorizationScope> authorizationScopeList){this.authorizationScopeList = authorizationScopeList;}publicList<String>getTokenUrlList(){return tokenUrlList;}publicvoidsetTokenUrlList(List<String> tokenUrlList){this.tokenUrlList = tokenUrlList;}}publicstaticclassAuthorizationScope{/**
         * 作用域名称
         */privateString scope ="";/**
         * 作用域描述
         */privateString description ="";publicStringgetScope(){return scope;}publicvoidsetScope(String scope){this.scope = scope;}publicStringgetDescription(){return description;}publicvoidsetDescription(String description){this.description = description;}}
/**
 * swagger 资源映射路径
 * 
 */@ConfigurationpublicclassSwaggerWebConfigurationimplementsWebMvcConfigurer{@OverridepublicvoidaddResourceHandlers(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:Failedtostart 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)
    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(NativeMethod)
    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:null
    at 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 不兼容问题的处理
 *
 */@ComponentpublicclassSwaggerBeanPostProcessorimplementsBeanPostProcessor{@OverridepublicObjectpostProcessAfterInitialization(Object bean,String beanName)throwsBeansException{if(bean instanceofWebMvcRequestHandlerProvider|| bean instanceofWebFluxRequestHandlerProvider){customizeSpringfoxHandlerMappings(getHandlerMappings(bean));}return bean;}private<TextendsRequestMappingInfoHandlerMapping>voidcustomizeSpringfoxHandlerMappings(List<T> mappings){List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser()==null).collect(Collectors.toList());
        mappings.clear();
        mappings.addAll(copy);}@SuppressWarnings("unchecked")privateList<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){thrownewIllegalStateException(e);}}}

配置:

spring:mvc:pathmatch:matching-strategy: ant_path_matcher

在这里插入图片描述
心如欲壑,后土难填。

标签: spring boot java spring

本文转载自: https://blog.csdn.net/qq_35764295/article/details/129519144
版权归原作者 三省同学 所有, 如有侵权,请联系我们删除。

“SpringBoot整合Swagger3.0使用及报错解决大全”的评论:

还没有评论