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 | 42 | <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> |
| 43 | 43 | <!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) --> |
| 44 | 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 | 46 | <mapstruct.version>1.6.3</mapstruct.version> |
| 47 | 47 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 48 | 48 | </properties> | ... | ... |
yudao-dependencies/pom.xml
| ... | ... | @@ -17,10 +17,10 @@ |
| 17 | 17 | <revision>2025.08-SNAPSHOT</revision> |
| 18 | 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 | 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 | 24 | <!-- DB 相关 --> |
| 25 | 25 | <druid.version>1.2.24</druid.version> |
| 26 | 26 | <mybatis.version>3.5.19</mybatis.version> |
| ... | ... | @@ -39,7 +39,7 @@ |
| 39 | 39 | <lock4j.version>2.2.7</lock4j.version> |
| 40 | 40 | <!-- 监控相关 --> |
| 41 | 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 | 43 | <opentracing.version>0.33.0</opentracing.version> |
| 44 | 44 | <!-- Test 测试相关 --> |
| 45 | 45 | <podam.version>8.0.2.RELEASE</podam.version> |
| ... | ... | @@ -151,13 +151,19 @@ |
| 151 | 151 | </dependency> |
| 152 | 152 | |
| 153 | 153 | <dependency> |
| 154 | - <groupId>com.github.xingfudeshi</groupId> <!-- TODO 芋艿:https://github.com/xiaoymin/knife4j/issues/874 --> | |
| 154 | + <groupId>com.github.xiaoymin</groupId> | |
| 155 | 155 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
| 156 | 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 | 163 | </dependency> |
| 158 | 164 | <dependency> |
| 159 | 165 | <groupId>org.springdoc</groupId> |
| 160 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | |
| 166 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | |
| 161 | 167 | <version>${springdoc.version}</version> |
| 162 | 168 | </dependency> |
| 163 | 169 | ... | ... |
yudao-framework/yudao-common/pom.xml
| ... | ... | @@ -60,7 +60,7 @@ |
| 60 | 60 | |
| 61 | 61 | <dependency> |
| 62 | 62 | <groupId>org.springdoc</groupId> |
| 63 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | |
| 63 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | |
| 64 | 64 | <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> |
| 65 | 65 | </dependency> |
| 66 | 66 | ... | ... |
yudao-framework/yudao-spring-boot-starter-web/pom.xml
| ... | ... | @@ -39,12 +39,12 @@ |
| 39 | 39 | </dependency> |
| 40 | 40 | |
| 41 | 41 | <dependency> |
| 42 | - <groupId>com.github.xingfudeshi</groupId> | |
| 42 | + <groupId>com.github.xiaoymin</groupId> | |
| 43 | 43 | <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
| 44 | 44 | </dependency> |
| 45 | 45 | <dependency> |
| 46 | 46 | <groupId>org.springdoc</groupId> |
| 47 | - <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | |
| 47 | + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> | |
| 48 | 48 | </dependency> |
| 49 | 49 | |
| 50 | 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 | 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 | 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 24 | 24 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 25 | 25 | import org.springframework.context.annotation.Bean; |
| 26 | +import org.springframework.context.annotation.Import; | |
| 26 | 27 | import org.springframework.context.annotation.Primary; |
| 27 | 28 | import org.springframework.http.HttpHeaders; |
| 28 | 29 | |
| ... | ... | @@ -46,6 +47,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ |
| 46 | 47 | @ConditionalOnClass({OpenAPI.class}) |
| 47 | 48 | @EnableConfigurationProperties(SwaggerProperties.class) |
| 48 | 49 | @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 |
| 50 | +@Import(Knife4jOpenApiCustomizer.class) | |
| 49 | 51 | public class YudaoSwaggerAutoConfiguration { |
| 50 | 52 | |
| 51 | 53 | // ========== 全局 OpenAPI 配置 ========== | ... | ... |
yudao-server/src/main/resources/application.yaml
| ... | ... | @@ -48,7 +48,7 @@ springdoc: |
| 48 | 48 | default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 |
| 49 | 49 | |
| 50 | 50 | knife4j: |
| 51 | - enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 | |
| 51 | + enable: true | |
| 52 | 52 | setting: |
| 53 | 53 | language: zh_cn |
| 54 | 54 | ... | ... |