Springfox、Springdoc和Swagger

打印 上一主题 下一主题

主题 975|帖子 975|积分 2925

Springfox、Swagger 和 Springdoc

Springfox、Swagger 和 Springdoc 是用于在 Spring Boot 项目中生成API文档的工具,但它们之间有明显的区别和演进关系:
1.Swagger

简介



  • Swagger 是一个开源项目,旨在为 RESTful APIs 提供交互式文档。
  • 最早由 SmartBear 开发,后来演进为 OpenAPI 规范 的前身。
  • Swagger 的焦点组件包罗:

    • Swagger UI:提供交互式的 Web 界面,展示 API 端点并答应直接调用测试。
    • Swagger Editor:编写和查看 OpenAPI 形貌文件的工具。
    • Swagger Codegen:基于 API 形貌文件生成客户端和服务端代码。

与 Spring 的关系

Swagger 本身不依赖 Spring,但通过扩展工具(如 Springfox)使其在 Spring 框架中得到利用。
   Swagger2是Swagger规范的一个实现
  Swagger3是基于OpenAPI规范的新版本,它是Swagger规范的后续标准,
提供了更好的可扩展性和更丰富的功能。
  Spring Boot从2.6.0版本开始不再原生支持Swagger2,由于Spring官方的更新导致了与Swagger2的不兼容。
开发者须要利用Springdoc OpenAPI库来替代Springfox,以在Spring Boot 2.6.0及以上版本中集成OpenAPI文档。
Springdoc OpenAPI提供了对OpenAPI 3.0规范的支持,而且与Spring Boot的新版本兼容。
  2.Springfox

简介



  • Springfox 是一个专门为 Spring Boot 集成 Swagger 的库。
  • 焦点功能:扫描 Spring 项目中的注解和设置,生成基于 Swagger 的 API 文档。
  • 特点:

    • 支持 Spring MVC 和 Spring WebFlux。
    • 利用 @ApiOperation 和 @ApiModel 等注解来生成文档。
    • 支持 Swagger 2 和部分 OpenAPI 3 特性。

现状



  • 停止活跃维护:Springfox 项目在 2021 年后维护频率大幅降低,社区对它的支持渐渐减少。3.0.x版本以后没有再更新
  • 兼容性标题:

    • 与 Spring Boot 2.6.x 和更高版本存在兼容性标题,主要是由于 Springfox 利用的 RequestMappingHandlerMapping 被 Spring Framework 的 Web 模块改动影响。

何时利用



  • 如果项目是基于 Spring Boot 2.5.x 或更早版本,而且已经利用了 Springfox,可以暂时保留。
  • 对于新项目,不发起继续利用 Springfox。
3.Springdoc

简介



  • Springdoc 是一个现代化工具,基于 OpenAPI 3 规范设计,替代 Springfox。
  • 提供与 Spring Boot 的无缝集成:

    • 主动生成 OpenAPI 3 文档。
    • 提供嵌入式的 Swagger UI(无需单独设置)。
    • 兼容 Spring MVC 和 Spring WebFlux。

优点



  • 强盛兼容性

    • 与 Spring Boot 2.x 和 3.x 完美兼容。
    • 支持 Spring Framework 的最新功能,例如相应式流和新注解模型。

  • 零设置:

    • 大部分功能开箱即用,减少了复杂的注解和设置需求。

  • 社区活跃:

    • 相比 Springfox,Springdoc 项目更活跃,一连发布新版本,解决社区反馈。

利用方式

1.添加 Maven 依赖:
  1. <dependency>
  2. <groupId>org.springdoc</groupId>
  3. <artifactId>springdoc-openapi-ui</artifactId>
  4. <version>最新版本</version>
  5. </dependency>
复制代码
2.启用后,访问默认路径 http://localhost:8080/swagger-ui.html。
总结:它们的关系与选择

工具关系实用场景当前发起Swagger基础规范和工具原始工具,用于标准化 API 文档用于 OpenAPI 标准支持SpringfoxSwagger 的 Spring 集成实现传统项目(Spring Boot 2.5.x)不再推荐,已过时Springdoc基于 OpenAPI 3 的现代化替代工具新项目,支持最新的 Spring Boot强烈推荐 迁移发起:从 Springfox 到 Springdoc

如果你当前利用 Springfox,但须要升级 Spring Boot 或改进文档支持,可以迁移到 Springdoc:

  • 替换依赖:

    • 移除 springfox-swagger2 和 springfox-swagger-ui。
    • 添加 springdoc-openapi-ui。

  • 注解适配:

    • Springdoc 支持 OpenAPI 3 的注解,通常是标准的 JSR-303 和 Spring 注解。
    • 替换 @ApiOperation 为 @Operation,替换 @ApiModel 为标准注解。

  • 设置改动:

    • Springdoc 险些不须要额外设置,大部分文档生成会主动完成。

结论:对于新项目,推荐利用 Springdoc;对于维护中的老项目,可以逐步迁移到 Springdoc,以便享受最新功能和更好的兼容性。
利用springboot2.6+版本和swagger2不兼容的解决方案

以下是一个springboot2.7.10集成swagger2和knife4j的例子
1、引入knife4j
  1. <dependency>
  2.      <groupId>com.github.xiaoymin</groupId>
  3.      <artifactId>knife4j-spring-boot-starter</artifactId>
  4.      <version>3.0.3</version>
  5. </dependency>
复制代码
knife4j官方发起开发者不要利用

2、knife4j设置文件
  1. import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import springfox.documentation.builders.ApiInfoBuilder;
  5. import springfox.documentation.builders.PathSelectors;
  6. import springfox.documentation.builders.RequestHandlerSelectors;
  7. import springfox.documentation.service.*;
  8. import springfox.documentation.spi.DocumentationType;
  9. import springfox.documentation.spi.service.contexts.SecurityContext;
  10. import springfox.documentation.spring.web.plugins.Docket;
  11. import springfox.documentation.swagger2.annotations.EnableSwagger2;
  12. import java.util.ArrayList;
  13. import java.util.Collections;
  14. import java.util.List;
  15. @Configuration
  16. @EnableSwagger2
  17. @EnableKnife4j
  18. public class Knife4jConfig {
  19.     @Bean
  20.     public Docket createRestApi() {
  21.         return new Docket(DocumentationType.SWAGGER_2)
  22.                 .useDefaultResponseMessages(false)
  23.                 .enable(true)
  24.                 .apiInfo(apiInfo())
  25.                 .select()
  26.                 //.apis(RequestHandlerSelectors.basePackage("com.linear.visual.controller"))
  27.                 .apis(RequestHandlerSelectors.any())
  28.                 .paths(PathSelectors.any())
  29.                 .build()
  30.                 .securitySchemes(Collections.singletonList(securityScheme()))
  31.                 .securityContexts(securityContexts());
  32.     }
  33.     private ApiInfo apiInfo() {
  34.         return new ApiInfoBuilder()
  35.                 .description("可视化接口")
  36.                 .contact(new Contact("Linear", "linear", "linear"))
  37.                 .version("v1.0.0")
  38.                 .title("API测试文档")
  39.                 .build();
  40.     }
  41.     @Bean
  42.     SecurityScheme securityScheme() {
  43.         return new ApiKey("Authorization", "Authorization", "header");
  44.     }
  45.     /**
  46.      * 安全上下文
  47.      */
  48.     private List<SecurityContext> securityContexts() {
  49.         List<SecurityContext> securityContexts = new ArrayList<>();
  50.         securityContexts.add(
  51.                 SecurityContext.builder()
  52.                         .securityReferences(defaultAuth())
  53.                         .build());
  54.         return securityContexts;
  55.     }
  56.     /**
  57.      * 默认的安全上引用
  58.      */
  59.     private List<SecurityReference> defaultAuth() {
  60.         AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
  61.         AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
  62.         authorizationScopes[0] = authorizationScope;
  63.         List<SecurityReference> securityReferences = new ArrayList<>();
  64.         securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
  65.         return securityReferences;
  66.     }
  67. }
复制代码
如果利用低版本的springboot好比2.6以下的,基本上都可以成功的。
但是利用2.6以上版本,这个时候大概会报启动失败
  1. org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
  2.         at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.26.jar:5.3.26]
  3.         at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.26.jar:5.3.26]
  4.         at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.26.jar:5.3.26]
  5.         at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
  6.         at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.26.jar:5.3.26]
  7.         at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.26.jar:5.3.26]
  8.         at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:937) ~[spring-context-5.3.26.jar:5.3.26]
  9.         at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.26.jar:5.3.26]
  10.         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.10.jar:2.7.10]
  11.         at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.10.jar:2.7.10]
  12.         at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.10.jar:2.7.10]
  13.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.10.jar:2.7.10]
  14.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.10.jar:2.7.10]
  15.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.10.jar:2.7.10]
  16.         at com.asr.hedu.fsp.service.oms.course.OmsCourseBootstrap.main(OmsCourseBootstrap.java:47) ~[classes/:na]
  17. Caused by: java.lang.NullPointerException: null
  18.         at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
  19.         at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) ~[springfox-core-3.0.0.jar:3.0.0]
  20.         at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) ~[springfox-spi-3.0.0.jar:3.0.0]
  21.         at java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) ~[na:na]
  22.         at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:na]
  23.         at java.base/java.util.TimSort.sort(TimSort.java:234) ~[na:na]
  24.         at java.base/java.util.Arrays.sort(Arrays.java:1515) ~[na:na]
  25.         at java.base/java.util.ArrayList.sort(ArrayList.java:1750) ~[na:na]
  26.         at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:392) ~[na:na]
  27.         at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
  28.         at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
  29.         at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
  30.         at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
  31.         at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) ~[na:na]
  32.         at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
  33.         at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
  34.         at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
  35.         at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
  36.         at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
  37.         at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
  38.         at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655) ~[na:na]
  39.         at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
  40.         at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
  41.         at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
  42.         at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
  43.         at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
  44.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107) ~[springfox-spring-web-3.0.0.jar:3.0.0]
  45.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91) ~[springfox-spring-web-3.0.0.jar:3.0.0]
  46.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82) ~[springfox-spring-web-3.0.0.jar:3.0.0]
  47.         at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100) ~[springfox-spring-web-3.0.0.jar:3.0.0]
  48.         at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.26.jar:5.3.26]
  49.         ... 14 common frames omitted
复制代码
这个标题经查询跟springboot的bean初始化有关系,后续的版本更改了相关的东西,具体是什么没有细究,反正参照stackoverflow中的修改方法,参加一下设置类就可以避免错误
  1. import org.springframework.beans.BeansException;
  2. import org.springframework.beans.factory.config.BeanPostProcessor;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.lang.NonNull;
  6. import org.springframework.util.ReflectionUtils;
  7. import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
  8. import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
  9. import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
  10. import java.lang.reflect.Field;
  11. import java.util.List;
  12. import java.util.stream.Collectors;
  13. /**
  14. * swagger config
  15. *
  16. * @author zxl
  17. * @date 2023-05-16
  18. */
  19. @Configuration
  20. public class SwaggerConfig {
  21.     @Bean
  22.     public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
  23.         return new BeanPostProcessor() {
  24.             @Override
  25.             public Object postProcessAfterInitialization(@NonNull  Object bean, @NonNull String beanName) throws BeansException {
  26.                 if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
  27.                     customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
  28.                 }
  29.                 return bean;
  30.             }
  31.             private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
  32.                 List<T> copy = mappings.stream()
  33.                         .filter(mapping -> mapping.getPatternParser() == null)
  34.                         .collect(Collectors.toList());
  35.                 mappings.clear();
  36.                 mappings.addAll(copy);
  37.             }
  38.             @SuppressWarnings("unchecked")
  39.             private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
  40.                 try {
  41.                     Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
  42.                     assert field != null;
  43.                     field.setAccessible(true);
  44.                     return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
  45.                 } catch (IllegalArgumentException | IllegalAccessException e) {
  46.                     throw new IllegalStateException(e);
  47.                 }
  48.             }
  49.         };
  50.     }
  51. }
复制代码
参加后启动正常,但是访问swagger的/v2/api-docs接口,发现没有得到数据,接口内容是空的。
这是通过搜索得到有几种解决方案
1、ShortVideoSwagger2的设置类须要集成WebMvcConfigurationSupport
2、ShortVideoSwagger2的设置类增加@EnableWebMvc注解
3、springboot的设置增加增加一下设置
  1. spring:
  2.   mvc:
  3.     pathmatch:
  4.       matching-strategy: ANT_PATH_MATCHE
复制代码
通过试验得知,这三种接口都可以解决,但是前两种是有副总用的
@EnableWebMvc发起慎用,最后在非springboot项目中利用
前两种解决方案会粉碎springboot对springwebmvc的主动装配,导致自界说的一些convertor或者ObjectMapper失效。
如今我的项目中是自界说的ObjectMapper失效。以是最后利用第三种方式,后期的springboot版本的matching-strategy默认的改为了PATH_PATTERN_PARSER,把它改为ANT_PATH_MATCHER就可以了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

半亩花草

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表