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 依赖:
- <dependency>
- <groupId>org.springdoc</groupId>
- <artifactId>springdoc-openapi-ui</artifactId>
- <version>最新版本</version>
- </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
- <dependency>
- <groupId>com.github.xiaoymin</groupId>
- <artifactId>knife4j-spring-boot-starter</artifactId>
- <version>3.0.3</version>
- </dependency>
复制代码 knife4j官方发起开发者不要利用

2、knife4j设置文件
- import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import springfox.documentation.builders.ApiInfoBuilder;
- import springfox.documentation.builders.PathSelectors;
- import springfox.documentation.builders.RequestHandlerSelectors;
- import springfox.documentation.service.*;
- import springfox.documentation.spi.DocumentationType;
- import springfox.documentation.spi.service.contexts.SecurityContext;
- import springfox.documentation.spring.web.plugins.Docket;
- import springfox.documentation.swagger2.annotations.EnableSwagger2;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- @Configuration
- @EnableSwagger2
- @EnableKnife4j
- public class Knife4jConfig {
- @Bean
- public Docket createRestApi() {
- return new Docket(DocumentationType.SWAGGER_2)
- .useDefaultResponseMessages(false)
- .enable(true)
- .apiInfo(apiInfo())
- .select()
- //.apis(RequestHandlerSelectors.basePackage("com.linear.visual.controller"))
- .apis(RequestHandlerSelectors.any())
- .paths(PathSelectors.any())
- .build()
- .securitySchemes(Collections.singletonList(securityScheme()))
- .securityContexts(securityContexts());
- }
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .description("可视化接口")
- .contact(new Contact("Linear", "linear", "linear"))
- .version("v1.0.0")
- .title("API测试文档")
- .build();
- }
- @Bean
- SecurityScheme securityScheme() {
- return new ApiKey("Authorization", "Authorization", "header");
- }
- /**
- * 安全上下文
- */
- private List<SecurityContext> securityContexts() {
- List<SecurityContext> securityContexts = new ArrayList<>();
- securityContexts.add(
- SecurityContext.builder()
- .securityReferences(defaultAuth())
- .build());
- return securityContexts;
- }
- /**
- * 默认的安全上引用
- */
- private List<SecurityReference> defaultAuth() {
- AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
- AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
- authorizationScopes[0] = authorizationScope;
- List<SecurityReference> securityReferences = new ArrayList<>();
- securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
- return securityReferences;
- }
- }
复制代码 如果利用低版本的springboot好比2.6以下的,基本上都可以成功的。
但是利用2.6以上版本,这个时候大概会报启动失败
- 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) ~[spring-context-5.3.26.jar:5.3.26]
- at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.26.jar:5.3.26]
- at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.26.jar:5.3.26]
- at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
- at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.26.jar:5.3.26]
- at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.26.jar:5.3.26]
- at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:937) ~[spring-context-5.3.26.jar:5.3.26]
- at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.26.jar:5.3.26]
- at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.10.jar:2.7.10]
- at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.10.jar:2.7.10]
- at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.10.jar:2.7.10]
- at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.10.jar:2.7.10]
- at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.10.jar:2.7.10]
- at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.10.jar:2.7.10]
- at com.asr.hedu.fsp.service.oms.course.OmsCourseBootstrap.main(OmsCourseBootstrap.java:47) ~[classes/:na]
- Caused by: java.lang.NullPointerException: null
- at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
- at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) ~[springfox-core-3.0.0.jar:3.0.0]
- at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) ~[springfox-spi-3.0.0.jar:3.0.0]
- at java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) ~[na:na]
- at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:na]
- at java.base/java.util.TimSort.sort(TimSort.java:234) ~[na:na]
- at java.base/java.util.Arrays.sort(Arrays.java:1515) ~[na:na]
- at java.base/java.util.ArrayList.sort(ArrayList.java:1750) ~[na:na]
- at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:392) ~[na:na]
- at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
- at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
- at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
- at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
- at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) ~[na:na]
- at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
- at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
- at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
- at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
- at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
- at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
- at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655) ~[na:na]
- at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
- at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
- at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
- at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
- at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
- at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107) ~[springfox-spring-web-3.0.0.jar:3.0.0]
- at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91) ~[springfox-spring-web-3.0.0.jar:3.0.0]
- at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82) ~[springfox-spring-web-3.0.0.jar:3.0.0]
- at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100) ~[springfox-spring-web-3.0.0.jar:3.0.0]
- at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.26.jar:5.3.26]
- ... 14 common frames omitted
复制代码 这个标题经查询跟springboot的bean初始化有关系,后续的版本更改了相关的东西,具体是什么没有细究,反正参照stackoverflow中的修改方法,参加一下设置类就可以避免错误
- 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.lang.NonNull;
- 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;
- /**
- * swagger config
- *
- * @author zxl
- * @date 2023-05-16
- */
- @Configuration
- public class SwaggerConfig {
- @Bean
- public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
- return new BeanPostProcessor() {
- @Override
- public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull 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");
- assert field != null;
- field.setAccessible(true);
- return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
- } catch (IllegalArgumentException | IllegalAccessException e) {
- throw new IllegalStateException(e);
- }
- }
- };
- }
- }
复制代码 参加后启动正常,但是访问swagger的/v2/api-docs接口,发现没有得到数据,接口内容是空的。
这是通过搜索得到有几种解决方案
1、ShortVideoSwagger2的设置类须要集成WebMvcConfigurationSupport
2、ShortVideoSwagger2的设置类增加@EnableWebMvc注解
3、springboot的设置增加增加一下设置
- spring:
- mvc:
- pathmatch:
- matching-strategy: ANT_PATH_MATCHE
复制代码 通过试验得知,这三种接口都可以解决,但是前两种是有副总用的
@EnableWebMvc发起慎用,最后在非springboot项目中利用
前两种解决方案会粉碎springboot对springwebmvc的主动装配,导致自界说的一些convertor或者ObjectMapper失效。
如今我的项目中是自界说的ObjectMapper失效。以是最后利用第三种方式,后期的springboot版本的matching-strategy默认的改为了PATH_PATTERN_PARSER,把它改为ANT_PATH_MATCHER就可以了。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |