Commit e91fc70f87b8f02f6013ee5170635a2e7f1eb3d8
1 parent
236d9536
feat:【framework 框架】兼容 knife4j 4.5.0 版本,升级 spring boot 到 3.5.4
Showing
7 changed files
with
165 additions
and
11 deletions
pom.xml
| @@ -42,7 +42,7 @@ | @@ -42,7 +42,7 @@ | ||
| 42 | <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> | 42 | <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> |
| 43 | <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) --> | 43 | <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) --> |
| 44 | <lombok.version>1.18.38</lombok.version> | 44 | <lombok.version>1.18.38</lombok.version> |
| 45 | - <spring.boot.version>3.4.5</spring.boot.version> | 45 | + <spring.boot.version>3.5.4</spring.boot.version> |
| 46 | <mapstruct.version>1.6.3</mapstruct.version> | 46 | <mapstruct.version>1.6.3</mapstruct.version> |
| 47 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | 47 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 48 | </properties> | 48 | </properties> |
yudao-dependencies/pom.xml
| @@ -17,10 +17,10 @@ | @@ -17,10 +17,10 @@ | ||
| 17 | <revision>2025.08-SNAPSHOT</revision> | 17 | <revision>2025.08-SNAPSHOT</revision> |
| 18 | <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> | 18 | <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> |
| 19 | <!-- 统一依赖管理 --> | 19 | <!-- 统一依赖管理 --> |
| 20 | - <spring.boot.version>3.4.5</spring.boot.version> | 20 | + <spring.boot.version>3.5.4</spring.boot.version> |
| 21 | <!-- Web 相关 --> | 21 | <!-- Web 相关 --> |
| 22 | - <springdoc.version>2.8.3</springdoc.version> | ||
| 23 | - <knife4j.version>4.6.0</knife4j.version> | 22 | + <springdoc.version>2.8.9</springdoc.version> |
| 23 | + <knife4j.version>4.5.0</knife4j.version> | ||
| 24 | <!-- DB 相关 --> | 24 | <!-- DB 相关 --> |
| 25 | <druid.version>1.2.24</druid.version> | 25 | <druid.version>1.2.24</druid.version> |
| 26 | <mybatis.version>3.5.19</mybatis.version> | 26 | <mybatis.version>3.5.19</mybatis.version> |
| @@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
| 39 | <lock4j.version>2.2.7</lock4j.version> | 39 | <lock4j.version>2.2.7</lock4j.version> |
| 40 | <!-- 监控相关 --> | 40 | <!-- 监控相关 --> |
| 41 | <skywalking.version>9.0.0</skywalking.version> | 41 | <skywalking.version>9.0.0</skywalking.version> |
| 42 | - <spring-boot-admin.version>3.4.5</spring-boot-admin.version> | 42 | + <spring-boot-admin.version>3.5.2</spring-boot-admin.version> |
| 43 | <opentracing.version>0.33.0</opentracing.version> | 43 | <opentracing.version>0.33.0</opentracing.version> |
| 44 | <!-- Test 测试相关 --> | 44 | <!-- Test 测试相关 --> |
| 45 | <podam.version>8.0.2.RELEASE</podam.version> | 45 | <podam.version>8.0.2.RELEASE</podam.version> |
| @@ -151,13 +151,19 @@ | @@ -151,13 +151,19 @@ | ||
| 151 | </dependency> | 151 | </dependency> |
| 152 | 152 | ||
| 153 | <dependency> | 153 | <dependency> |
| 154 | - <groupId>com.github.xingfudeshi</groupId> <!-- TODO 芋艿:https://github.com/xiaoymin/knife4j/issues/874 --> | 154 | + <groupId>com.github.xiaoymin</groupId> |
| 155 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> | 155 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
| 156 | <version>${knife4j.version}</version> | 156 | <version>${knife4j.version}</version> |
| 157 | + <exclusions> | ||
| 158 | + <exclusion> | ||
| 159 | + <groupId>org.springdoc</groupId> | ||
| 160 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | ||
| 161 | + </exclusion> | ||
| 162 | + </exclusions> | ||
| 157 | </dependency> | 163 | </dependency> |
| 158 | <dependency> | 164 | <dependency> |
| 159 | <groupId>org.springdoc</groupId> | 165 | <groupId>org.springdoc</groupId> |
| 160 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | 166 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> |
| 161 | <version>${springdoc.version}</version> | 167 | <version>${springdoc.version}</version> |
| 162 | </dependency> | 168 | </dependency> |
| 163 | 169 |
yudao-framework/yudao-common/pom.xml
| @@ -60,7 +60,7 @@ | @@ -60,7 +60,7 @@ | ||
| 60 | 60 | ||
| 61 | <dependency> | 61 | <dependency> |
| 62 | <groupId>org.springdoc</groupId> | 62 | <groupId>org.springdoc</groupId> |
| 63 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | 63 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> |
| 64 | <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> | 64 | <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> |
| 65 | </dependency> | 65 | </dependency> |
| 66 | 66 |
yudao-framework/yudao-spring-boot-starter-web/pom.xml
| @@ -39,12 +39,12 @@ | @@ -39,12 +39,12 @@ | ||
| 39 | </dependency> | 39 | </dependency> |
| 40 | 40 | ||
| 41 | <dependency> | 41 | <dependency> |
| 42 | - <groupId>com.github.xingfudeshi</groupId> | 42 | + <groupId>com.github.xiaoymin</groupId> |
| 43 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> | 43 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
| 44 | </dependency> | 44 | </dependency> |
| 45 | <dependency> | 45 | <dependency> |
| 46 | <groupId>org.springdoc</groupId> | 46 | <groupId>org.springdoc</groupId> |
| 47 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | 47 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> |
| 48 | </dependency> | 48 | </dependency> |
| 49 | 49 | ||
| 50 | <dependency> | 50 | <dependency> |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/Knife4jOpenApiCustomizer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.swagger.config; | ||
| 2 | + | ||
| 3 | +import com.github.xiaoymin.knife4j.annotations.ApiSupport; | ||
| 4 | +import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants; | ||
| 5 | +import com.github.xiaoymin.knife4j.core.conf.GlobalConstants; | ||
| 6 | +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties; | ||
| 7 | +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting; | ||
| 8 | +import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver; | ||
| 9 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 10 | +import io.swagger.v3.oas.models.OpenAPI; | ||
| 11 | +import lombok.extern.slf4j.Slf4j; | ||
| 12 | +import org.apache.commons.lang3.ArrayUtils; | ||
| 13 | +import org.springdoc.core.customizers.GlobalOpenApiCustomizer; | ||
| 14 | +import org.springdoc.core.properties.SpringDocConfigProperties; | ||
| 15 | +import org.springframework.beans.factory.config.BeanDefinition; | ||
| 16 | +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; | ||
| 17 | +import org.springframework.context.annotation.Configuration; | ||
| 18 | +import org.springframework.context.annotation.Primary; | ||
| 19 | +import org.springframework.core.type.filter.AnnotationTypeFilter; | ||
| 20 | +import org.springframework.util.CollectionUtils; | ||
| 21 | +import org.springframework.web.bind.annotation.RestController; | ||
| 22 | + | ||
| 23 | +import java.lang.annotation.Annotation; | ||
| 24 | +import java.util.*; | ||
| 25 | +import java.util.stream.Collectors; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * 增强扩展属性支持 | ||
| 29 | + * | ||
| 30 | + * 参考 <a href="https://github.com/xiaoymin/knife4j/issues/913">Spring Boot 3.4 以上版本 /v3/api-docs 解决接口报错,依赖修复</a> | ||
| 31 | + * | ||
| 32 | + * @since 4.1.0 | ||
| 33 | + * @author <a href="xiaoymin@foxmail.com">xiaoymin@foxmail.com</a> | ||
| 34 | + * 2022/12/11 22:40 | ||
| 35 | + */ | ||
| 36 | +@Primary | ||
| 37 | +@Configuration | ||
| 38 | +@Slf4j | ||
| 39 | +public class Knife4jOpenApiCustomizer extends com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer | ||
| 40 | + implements GlobalOpenApiCustomizer { | ||
| 41 | + | ||
| 42 | + final Knife4jProperties knife4jProperties; | ||
| 43 | + final SpringDocConfigProperties properties; | ||
| 44 | + | ||
| 45 | + public Knife4jOpenApiCustomizer(Knife4jProperties knife4jProperties, SpringDocConfigProperties properties) { | ||
| 46 | + super(knife4jProperties,properties); | ||
| 47 | + this.knife4jProperties = knife4jProperties; | ||
| 48 | + this.properties = properties; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + @Override | ||
| 52 | + public void customise(OpenAPI openApi) { | ||
| 53 | + if (knife4jProperties.isEnable()) { | ||
| 54 | + Knife4jSetting setting = knife4jProperties.getSetting(); | ||
| 55 | + OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments()); | ||
| 56 | + // 解析初始化 | ||
| 57 | + openApiExtensionResolver.start(); | ||
| 58 | + Map<String, Object> objectMap = new HashMap<>(); | ||
| 59 | + objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting); | ||
| 60 | + objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles()); | ||
| 61 | + openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap); | ||
| 62 | + addOrderExtension(openApi); | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * 往 OpenAPI 内 tags 字段添加 x-order 属性 | ||
| 68 | + * | ||
| 69 | + * @param openApi openApi | ||
| 70 | + */ | ||
| 71 | + private void addOrderExtension(OpenAPI openApi) { | ||
| 72 | + if (CollectionUtils.isEmpty(properties.getGroupConfigs())) { | ||
| 73 | + return; | ||
| 74 | + } | ||
| 75 | + // 获取包扫描路径 | ||
| 76 | + Set<String> packagesToScan = | ||
| 77 | + properties.getGroupConfigs().stream() | ||
| 78 | + .map(SpringDocConfigProperties.GroupConfig::getPackagesToScan) | ||
| 79 | + .filter(toScan -> !CollectionUtils.isEmpty(toScan)) | ||
| 80 | + .flatMap(List::stream) | ||
| 81 | + .collect(Collectors.toSet()); | ||
| 82 | + if (CollectionUtils.isEmpty(packagesToScan)) { | ||
| 83 | + return; | ||
| 84 | + } | ||
| 85 | + // 扫描包下被 ApiSupport 注解的 RestController Class | ||
| 86 | + Set<Class<?>> classes = packagesToScan.stream() | ||
| 87 | + .map(packageToScan -> scanPackageByAnnotation(packageToScan, RestController.class)) | ||
| 88 | + .flatMap(Set::stream) | ||
| 89 | + .filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class)) | ||
| 90 | + .collect(Collectors.toSet()); | ||
| 91 | + if (!CollectionUtils.isEmpty(classes)) { | ||
| 92 | + // ApiSupport oder 值存入 tagSortMap<Tag.name,ApiSupport.order> | ||
| 93 | + Map<String, Integer> tagOrderMap = new HashMap<>(); | ||
| 94 | + classes.forEach(clazz -> { | ||
| 95 | + Tag tag = getTag(clazz); | ||
| 96 | + if (Objects.nonNull(tag)) { | ||
| 97 | + ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class); | ||
| 98 | + tagOrderMap.putIfAbsent(tag.name(), apiSupport.order()); | ||
| 99 | + } | ||
| 100 | + }); | ||
| 101 | + // 往 openApi tags 字段添加 x-order 增强属性 | ||
| 102 | + if (openApi.getTags() != null) { | ||
| 103 | + openApi.getTags().forEach(tag -> { | ||
| 104 | + if (tagOrderMap.containsKey(tag.getName())) { | ||
| 105 | + tag.addExtension(ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName())); | ||
| 106 | + } | ||
| 107 | + }); | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + private Tag getTag(Class<?> clazz) { | ||
| 113 | + // 从类上获取 | ||
| 114 | + Tag tag = clazz.getAnnotation(Tag.class); | ||
| 115 | + if (Objects.isNull(tag)) { | ||
| 116 | + // 从接口上获取 | ||
| 117 | + Class<?>[] interfaces = clazz.getInterfaces(); | ||
| 118 | + if (ArrayUtils.isNotEmpty(interfaces)) { | ||
| 119 | + for (Class<?> interfaceClazz : interfaces) { | ||
| 120 | + Tag anno = interfaceClazz.getAnnotation(Tag.class); | ||
| 121 | + if (Objects.nonNull(anno)) { | ||
| 122 | + tag = anno; | ||
| 123 | + break; | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + return tag; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + private Set<Class<?>> scanPackageByAnnotation(String packageName, final Class<? extends Annotation> annotationClass) { | ||
| 132 | + ClassPathScanningCandidateComponentProvider scanner = | ||
| 133 | + new ClassPathScanningCandidateComponentProvider(false); | ||
| 134 | + scanner.addIncludeFilter(new AnnotationTypeFilter(annotationClass)); | ||
| 135 | + Set<Class<?>> classes = new HashSet<>(); | ||
| 136 | + for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) { | ||
| 137 | + try { | ||
| 138 | + Class<?> clazz = Class.forName(beanDefinition.getBeanClassName()); | ||
| 139 | + classes.add(clazz); | ||
| 140 | + } catch (ClassNotFoundException ignore) { | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + return classes; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | +} | ||
| 0 | \ No newline at end of file | 147 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
| @@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | @@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
| 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 24 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 24 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 25 | import org.springframework.context.annotation.Bean; | 25 | import org.springframework.context.annotation.Bean; |
| 26 | +import org.springframework.context.annotation.Import; | ||
| 26 | import org.springframework.context.annotation.Primary; | 27 | import org.springframework.context.annotation.Primary; |
| 27 | import org.springframework.http.HttpHeaders; | 28 | import org.springframework.http.HttpHeaders; |
| 28 | 29 | ||
| @@ -46,6 +47,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ | @@ -46,6 +47,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ | ||
| 46 | @ConditionalOnClass({OpenAPI.class}) | 47 | @ConditionalOnClass({OpenAPI.class}) |
| 47 | @EnableConfigurationProperties(SwaggerProperties.class) | 48 | @EnableConfigurationProperties(SwaggerProperties.class) |
| 48 | @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 | 49 | @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 |
| 50 | +@Import(Knife4jOpenApiCustomizer.class) | ||
| 49 | public class YudaoSwaggerAutoConfiguration { | 51 | public class YudaoSwaggerAutoConfiguration { |
| 50 | 52 | ||
| 51 | // ========== 全局 OpenAPI 配置 ========== | 53 | // ========== 全局 OpenAPI 配置 ========== |
yudao-server/src/main/resources/application.yaml
| @@ -48,7 +48,7 @@ springdoc: | @@ -48,7 +48,7 @@ springdoc: | ||
| 48 | default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 | 48 | default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 |
| 49 | 49 | ||
| 50 | knife4j: | 50 | knife4j: |
| 51 | - enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 | 51 | + enable: true |
| 52 | setting: | 52 | setting: |
| 53 | language: zh_cn | 53 | language: zh_cn |
| 54 | 54 |