方法来源于knife4j的gitee issue评论区https://gitee.com/xiaoym/knife4j/issues/I7U2I0,网友@牛转乾坤提供
本人的 SpringBoot 版本 3.4.2,Knife4j 版本4.5.0(knife4j-openapi3-jakarta-spring-boot-starter)
核心是重写OpenAPIService,这里我把完整代码贴上来
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.tags.Tags;
import io.swagger.v3.oas.models.OpenAPI;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Component
public class MyOpenAPIService extends OpenAPIService {
private final PropertyResolverUtils propertyResolverUtils;
public MyOpenAPIService(Optional<OpenAPI> openAPI, SecurityService securityParser, SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers, Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers, Optional<JavadocProvider> javadocProvider, PropertyResolverUtils propertyResolverUtils1) {
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
this.propertyResolverUtils = propertyResolverUtils1;
}
/**
* 重写父类获取tags方法
*/
@Override
public void buildTagsFromClass(Class<?> beanType, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) {
Set<Tags> tagsSet = AnnotatedElementUtils.findAllMergedAnnotations(beanType, Tags.class);
Set<Tag> classTags = tagsSet.stream().flatMap((x) -> Stream.of(x.value())).collect(Collectors.toSet());
classTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(beanType, Tag.class));
if (!CollectionUtils.isEmpty(classTags)) {
tagsStr.addAll(classTags.stream().map((tag) -> this.propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet()));
List<Tag> allTags = new ArrayList<>(classTags);
this.addTags(beanType,allTags, tags, locale);
}
}
/**
* 根据父类方法,添加了beanType参数,方便获取类上其他注解
*/
private void addTags(Class<?> beanType,List<Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) {
ApiSupport apiSupport = AnnotationUtils.findAnnotation(beanType,ApiSupport.class);
Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils.getTags(sourceTags.toArray(new Tag[0]), false); //第二个参数填true会忽略不在@Tag里写descripton的接口分组
optionalTagSet.ifPresent(tagsSet -> tagsSet.forEach(tag -> {
tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
if(apiSupport != null){
tag.addExtension(ExtensionsConstants.EXTENSION_ORDER, apiSupport.order());
}
if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName())))
tags.add(tag);
}));
}
}
application.yml配置
springdoc:
swagger-ui:
path: /swagger-ui.html
operations-sorter: order
knife4j:
enable: true
setting:
language: zh_cn
production: false
最后在每个Controller类上加上@ApiSupport注解和@Tag注解就能排序了
示例代码
@ApiSupport(order = 1)
@Tag(name = "测试控制器接口", description = "测试控制器接口") // description参数可不填
@RestController
@RequestMapping("/test")
public class TestController {
@ApiOperationSupport(order = 1)
@GetMapping("/getData")
public String getData() {
return "hehehehehe";
}
}