Knife4jOpenApiCustomizer.java 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package com.aitori.config;
  2. import com.github.xiaoymin.knife4j.annotations.ApiSupport;
  3. import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;
  4. import com.github.xiaoymin.knife4j.core.conf.GlobalConstants;
  5. import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;
  6. import com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting;
  7. import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
  8. import io.swagger.v3.oas.annotations.tags.Tag;
  9. import io.swagger.v3.oas.models.OpenAPI;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.apache.commons.lang3.ArrayUtils;
  12. import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
  13. import org.springdoc.core.properties.SpringDocConfigProperties;
  14. import org.springframework.beans.factory.config.BeanDefinition;
  15. import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
  16. import org.springframework.context.annotation.Configuration;
  17. import org.springframework.context.annotation.Primary;
  18. import org.springframework.core.type.filter.AnnotationTypeFilter;
  19. import org.springframework.util.CollectionUtils;
  20. import org.springframework.web.bind.annotation.RestController;
  21. import java.util.*;
  22. import java.util.stream.Collectors;
  23. /**
  24. * <p>只是为了解决spring3.4版本的兼容问题</p>
  25. *
  26. * <p>增强扩展属性支持</p>
  27. *
  28. * @author <a href="xiaoymin@foxmail.com">xiaoymin@foxmail.com</a>
  29. * 2022/12/11 22:40
  30. * @since 4.1.0
  31. */
  32. @Primary
  33. @Configuration
  34. @Slf4j
  35. public class Knife4jOpenApiCustomizer extends com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer implements GlobalOpenApiCustomizer {
  36. final Knife4jProperties knife4jProperties;
  37. final SpringDocConfigProperties properties;
  38. public Knife4jOpenApiCustomizer(Knife4jProperties knife4jProperties, SpringDocConfigProperties properties) {
  39. super(knife4jProperties, properties);
  40. this.knife4jProperties = knife4jProperties;
  41. this.properties = properties;
  42. }
  43. @Override
  44. public void customise(OpenAPI openApi) {
  45. log.debug("Knife4j OpenApiCustomizer");
  46. if (knife4jProperties.isEnable()) {
  47. Knife4jSetting setting = knife4jProperties.getSetting();
  48. OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());
  49. // 解析初始化
  50. openApiExtensionResolver.start();
  51. Map<String, Object> objectMap = new HashMap<>();
  52. objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting);
  53. objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles());
  54. openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap);
  55. addOrderExtension(openApi);
  56. }
  57. }
  58. /**
  59. * 往OpenAPI内tags字段添加x-order属性
  60. *
  61. * @param openApi openApi
  62. */
  63. private void addOrderExtension(OpenAPI openApi) {
  64. if (CollectionUtils.isEmpty(properties.getGroupConfigs())) {
  65. return;
  66. }
  67. // 获取包扫描路径
  68. Set<String> packagesToScan =
  69. properties.getGroupConfigs().stream()
  70. .map(SpringDocConfigProperties.GroupConfig::getPackagesToScan)
  71. .filter(toScan -> !CollectionUtils.isEmpty(toScan))
  72. .flatMap(List::stream)
  73. .collect(Collectors.toSet());
  74. if (CollectionUtils.isEmpty(packagesToScan)) {
  75. return;
  76. }
  77. // 扫描包下被ApiSupport注解的RestController Class
  78. Set<Class<?>> classes =
  79. packagesToScan.stream()
  80. .map(this::scanPackageByAnnotation)
  81. .flatMap(Set::stream)
  82. .filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class))
  83. .collect(Collectors.toSet());
  84. if (!CollectionUtils.isEmpty(classes)) {
  85. // ApiSupport oder值存入tagSortMap<Tag.name,ApiSupport.order>
  86. Map<String, Integer> tagOrderMap = new HashMap<>();
  87. classes.forEach(
  88. clazz -> {
  89. Tag tag = getTag(clazz);
  90. if (Objects.nonNull(tag)) {
  91. ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class);
  92. tagOrderMap.putIfAbsent(tag.name(), apiSupport.order());
  93. }
  94. });
  95. // 往openApi tags字段添加x-order增强属性
  96. if (openApi.getTags() != null) {
  97. openApi
  98. .getTags()
  99. .forEach(
  100. tag -> {
  101. if (tagOrderMap.containsKey(tag.getName())) {
  102. tag.addExtension(
  103. ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName()));
  104. }
  105. });
  106. }
  107. }
  108. }
  109. private Tag getTag(Class<?> clazz) {
  110. // 从类上获取
  111. Tag tag = clazz.getAnnotation(Tag.class);
  112. if (Objects.isNull(tag)) {
  113. // 从接口上获取
  114. Class<?>[] interfaces = clazz.getInterfaces();
  115. if (ArrayUtils.isNotEmpty(interfaces)) {
  116. for (Class<?> interfaceClazz : interfaces) {
  117. Tag anno = interfaceClazz.getAnnotation(Tag.class);
  118. if (Objects.nonNull(anno)) {
  119. tag = anno;
  120. break;
  121. }
  122. }
  123. }
  124. }
  125. return tag;
  126. }
  127. private Set<Class<?>> scanPackageByAnnotation(
  128. String packageName) {
  129. ClassPathScanningCandidateComponentProvider scanner =
  130. new ClassPathScanningCandidateComponentProvider(false);
  131. scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
  132. Set<Class<?>> classes = new HashSet<>();
  133. for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) {
  134. try {
  135. Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
  136. classes.add(clazz);
  137. } catch (ClassNotFoundException ignore) {
  138. }
  139. }
  140. return classes;
  141. }
  142. }