项目环境

springBoot 2.4.0
jdk1.8
swagger3
knife4j(Swagger生成Api文档的增强解决方案)

引入的包

<!-- springfox swagger3.x -->
<dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version>
</dependency>
<!-- springfox swagger3.x 整合Knife4j -->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</version><!-- 排除一些与swagger冲突的包 --><exclusions><exclusion><artifactId>swagger-annotations</artifactId><groupId>io.swagger</groupId></exclusion><exclusion><artifactId>swagger-models</artifactId><groupId>io.swagger</groupId></exclusion></exclusions>
</dependency>

编写一个可在方法和类上添加的注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** ApiVersion* 自定义swagger接口上的版本分组注解* 需要新分组时在下面的Version枚举类中新增一个常量即可** @author 七濑武* @date 2021/4/16 16:50*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE_USE})
public @interface ApiVersion {Version[] value();enum Version {/*** 分组名称*/DEFAULT("default"),v_1_1_0("1.1.0");private final String display;Version(String display) {this.display = display;}public String getDisplay() {return display;}}
}

配置Swagger3

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import com.google.common.collect.ImmutableList;
import com.nanase.takeshi.annotion.ApiVersion;
import com.nanase.takeshi.annotion.PassToken;
import com.nanase.takeshi.constants.JwtConstant;
import com.nanase.takeshi.util.enums.SysCodeEnum;
import io.swagger.annotations.Api;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.HttpMethod;
import org.springframework.plugin.core.OrderAwarePluginRegistry;
import org.springframework.plugin.core.PluginRegistry;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.ResponseBuilder;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.DocumentationPlugin;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;/*** Swagger3Config* * @author 七濑武* @date 2021/4/16 16:50*/
@Primary //自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常。(只对接口的多个实现生效)覆盖swagger自己的配置
@Configuration //定义配置类
@EnableKnife4j //开启Knife4j
public class Swagger3Config extends DocumentationPluginsManager {//yml文件中配置的name@Value("${spring.application.name}")private String applicationName;//注入Knife4jprivate final OpenApiExtensionResolver openApiExtensionResolver;//注入Knife4j@Autowiredpublic Swagger3Config(OpenApiExtensionResolver openApiExtensionResolver) {this.openApiExtensionResolver = openApiExtensionResolver;}@Overridepublic Collection<DocumentationPlugin> documentationPlugins() throws IllegalStateException {List<DocumentationPlugin> plugins = registry().getPlugins();ensureNoDuplicateGroups(plugins);return plugins.isEmpty() ? Collections.singleton(this.defaultDocumentationPlugin()) : plugins;}private void ensureNoDuplicateGroups(List<DocumentationPlugin> allPlugins) throws IllegalStateException {Map<String, List<DocumentationPlugin>> plugins = allPlugins.stream().collect(Collectors.groupingBy((input) -> {return Optional.ofNullable(input.getGroupName()).orElse("default");}, LinkedHashMap::new, Collectors.toList()));Iterable<String> duplicateGroups = plugins.entrySet().stream().filter((input) -> {return (input.getValue()).size() > 1;}).map(Map.Entry::getKey).collect(Collectors.toList());if (StreamSupport.stream(duplicateGroups.spliterator(), false).count() > 0L) {throw new IllegalStateException(String.format("Multiple Dockets with the same group name are not supported. The following duplicate groups were discovered. %s", String.join(",", duplicateGroups)));}}private DocumentationPlugin defaultDocumentationPlugin() {return new Docket(DocumentationType.OAS_30);}private SwaggerPluginRegistry registry() {List<Docket> list = new ArrayList<>();for (ApiVersion.Version version : ApiVersion.Version.values()) {Docket docket = new Docket(DocumentationType.OAS_30)// 指定构建api文档的详细信息的方法:apiInfo().apiInfo(apiInfo()).groupName(version.getDisplay()).select().apis(input -> {if (ApiVersion.Version.DEFAULT.equals(version)) {//指定扫描有Api注解的类return input.findControllerAnnotation(Api.class).isPresent();}//指定扫描有此版本的ApiVersion注解的方法return input.findAnnotation(ApiVersion.class).filter(item -> Arrays.asList(item.value()).contains(version)).isPresent();}).paths(PathSelectors.any()).build()//使Knife4j的增强配置生效.extensions(openApiExtensionResolver.buildSettingExtensions())// 支持的通讯协议集合.protocols(Stream.of("https", "http").collect(Collectors.toSet()))// 授权信息设置,必要的header token等认证信息.securitySchemes(securitySchemes())// 授权信息全局应用.securityContexts(securityContexts());list.add(docket);}return new SwaggerPluginRegistry(list, new AnnotationAwareOrderComparator());}private ApiInfo apiInfo() {return new ApiInfoBuilder()// 设置页面标题.title(applicationName)// 设置接口描述.description(applicationName + "通用框架接口")// 设置联系方式.contact(new Contact("七濑武", null, null)).build();}/*** 设置授权信息*/private List<SecurityScheme> securitySchemes() {//swagger3此处有个小坑(ApiKey中的参数name和keyname有问题,只有第一个参数name会生效,第二个参数keyname无效,此处就配置一样的字符串)return Collections.singletonList(new ApiKey("token", "token", In.HEADER.toValue()));}/*** 授权信息全局应用*/private List<SecurityContext> securityContexts() {return ImmutableList.of(SecurityContext.builder().securityReferences(Collections.singletonList(SecurityReference.builder().scopes(new AuthorizationScope[0])//此处需要配置的与ApiKey中的name一致才可以全局应用上.reference("token").build()))//声明作用域,@PassToken注解的方法不在header中添加token,全局应用时不应用在有@PassToken注解上面.operationSelector(o -> !o.findAnnotation(PassToken.class).isPresent()).build());}}/*** SwaggerPluginRegistry** @author 七濑武* @date 2021/4/16 16:55*/
class SwaggerPluginRegistry extends OrderAwarePluginRegistry<DocumentationPlugin, DocumentationType> implements PluginRegistry<DocumentationPlugin, DocumentationType> {protected SwaggerPluginRegistry(List<Docket> plugins, Comparator<? super DocumentationPlugin> comparator) {super(plugins, comparator);}@Overridepublic List<DocumentationPlugin> getPlugins() {return super.getPlugins();}
}

下面是我用到的PassToken注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 过滤token校验注解* controller层方法上加上注解可不校验token** @author 七濑武* @date 2020/11/27 16:50*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {boolean required() default true;
}

下面是用到的yml配置

server:port: 8080
spring:application:name: NanaseTakeshi
# knife4j配置
knife4j:basic:username: adminpassword: adminenable: true #开启认证,访问接口文档时需要用户名密码访问enable: true # 开启增强配置setting: # 增强的配置信息enableOpenApi: falseenableFooter: false

接口版本动态分组例子

//传入对应的版本即可
@ApiVersion(ApiVersion.Version.v_1_2_0)
@ApiOperation("测试ApiVersion注解的方法")
@GetMapping("/test")
public String test(){return "Hello World";
}

Swagger3 版本动态分组相关推荐

  1. Android M版本和非M版本动态权限适配方案

    动态权限适配方案 1.  背景 目前在Google官方推出的最新的AndroidM版本上,对于应用权限的调用做了大大的修改.在M版本之前,应用App需要用到什么权限只需要在AndroidManifes ...

  2. 动态壁纸android,Android 十大最新版本动态壁纸大盘点

    Typography Wall 是一款以透明雨滴做背景,同时能展示时钟.日期和电量信息的动态壁纸.这款透明雨滴时钟动态壁纸Typography Wall确实做的很漂亮,自从wpclock出来后,越来越 ...

  3. C语言实现通讯录(静态版本+动态版本)

    当前使用VS2019编译器 首先建立2个源文件test.c和contect.c以及1个头文件contect.h 一.通讯录操作菜单 void menu() {printf("******** ...

  4. Revit二次实现多版本动态引用RevitAPI.dll、RevitAPIUI.dll、AdWindows.dll、UIFramework.dll

    1.使用NuGet添加:Revit_All_Main_Versions_API_x64(动态引用) 2.修改工程文件配置:.csproj 复制如下的配置项 <PropertyGroup Cond ...

  5. java随机动态分组_Java将一组数随机分组

    试题:一个班40个学生,请把学生随机分成8组 既然是随机分组,必然用到随机数,其分组思路是: 用一个链表存储这组数,随机获取A中的元素下标,然后打印这个数,并从A中移除,直到A中全部为空 import ...

  6. Android 6.0及以上版本动态申请权限,11权限

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {//安卓11文件权限// 先判断有没有权限if (Environment.isExter ...

  7. 用java实现动态排序_关于Java动态分组排序的问题(Android中需要将数据排序给RecyclerView使用)...

    问题描述 现在有这样一个TestNotification里面定义了这些字段 private long time; private String pkg; private String content; ...

  8. 版本动态 | Apache Linkis(Incubating) 计算中间件1.1.2 版本发布

    Linkis 1.1.2 版本简介 GitHub:https://github.com/apache/incubator-linkis 本次发布主要支持在无 HDFS 的环境下进行精简化部署(支持部分 ...

  9. 类似qq分组栏-动态分组

    .h   文件 (void)viewDidLoad {    [super viewDidLoad];    _tableView = [[UITableView alloc] initWithFra ...

  10. SSRS 动态设置分组依据及行组个数

    动态设置行组及行组级数 业务场景: A公司想在同一张BI报告中通过切换参数,分别从不同角度分析统计产品销售情况, 同时又想同时从两个角度或三个角度统计分析. 本场景主要涉及两个技能点: 1)  动态设 ...

最新文章

  1. WCF服务编程(4):《WCF服务编程》第3版中文版翻译结束,即将出版
  2. iTerm2配置自动通过堡垒机登录服务器
  3. Tensorflow: 保存和复原模型(save and restore)
  4. maven 部分命令
  5. 使用gradle-android搭建jenkins slave构建机器常见构建问题(一)
  6. java ajax多文件上传插件_ajaxFileUpload.js插件支持多文件上传的方法
  7. 对HashMap数据结构的理解——加载因子和初始容量
  8. 方法的重写、重载及隐藏
  9. Android Error:Could not find lottie.jar
  10. openfire源码编译后部署到linux
  11. delphi win64 DEBUG不能进预设断点的问题
  12. 42表盘直径是从哪测量_万用表测量电容容量的方
  13. matlab平衡小车数学模型PID,Simulink仿真
  14. 金蝶云星空根据采购价目表写入自定义采购入库单是否价目表字段
  15. 一篇带你使用latex 应对美赛论文书写
  16. uniapp重新渲染页面_uni-app里面使用uni.request请求并且渲染列表
  17. 前端基础-Ajax对象
  18. Downward API,它的作用是:让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息。
  19. 如何使用python编程抢京东优惠券 知乎_小猿圈Python之实现京东秒杀功能技巧
  20. 爬虫工具 AppCrawler

热门文章

  1. 如何开发一个App(Android)
  2. java研发微博营销
  3. java赛马游戏_Java程序之赛马游戏
  4. java制作报表统计_统计报表制作,怎样做报表统计
  5. go语法 — 多路选择操作符 select的用法
  6. 这次要是讲不明白Spring Cloud核心组件,那就白写了!
  7. ps钢笔抠图的的引用说明
  8. 在设备上启用开发者选项
  9. 简历python技能怎么写_老鸟教你如何写好技术简历
  10. 你真的了解性能压测中的SLA吗?