springboot整合Swagger2,实现对接口的版本管理

有目标就不怕路远。年轻人.无论你现在身在何方.重要的是你将要向何处去。只有明确的目标才能助你成功。没有目标的航船.任何方向的风对他来说都是逆风。因此,再遥远的旅程,只要有目标.就不怕路远。没有目标,哪来的劲头?一车尔尼雷夫斯基

导读:本篇文章讲解 springboot整合Swagger2,实现对接口的版本管理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

springboot整合Swagger2,实现对接口的版本管理

1. 需求

测试同学有需求,对于接口,之前的版本是1.0,现在是1.1版本,我想要知道1.1新增了什么接口,或者说修改了什么接口,怎么办?

2. 细化需求

我们用分组来实现,分组在代码里就是 Docket,如图

在这里插入图片描述

  • 我们希望有一个默认的分组,所有的接口都放在这里,并且是排在最前面的
  • 被标记的接口能归类到 v2.7.3里
  • 一个接口可以同时被归类到多个地方,比如同时属于默认、v2.7.3、v2.8
  • 用于标记的注解,可以放在方法上,相当于全部接口都标注了;有可以单独标注在某个方法;标注在方法具有较高的优先级

3. 如何实现

3.1 如何共存多套UI

其实是可以共存多个UI的,实际测试可行。注意有时候会发现其中一个UI能刷出来另外一个不行,可以稍微等一等再试,或者调换下一下两个UI的顺序再试下。总之我遇到过 “其中一个UI可以另外一个不行” 的情况,但是最终发现 “两个UI可以并存”

3.2 步骤

  1. 新建springboot项目,或者在已有项目,在pom.xml中增加

    <!-- Swagger2 BEGIN -->
    <!--
    swagger2包中swagger-models版本有bug,example为空串会爆出NumberFormatException,需要排除并引入高版本
    排除时将 swagger-models 和 swagger-annotations 整一对排除
    -->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.9.2</version>
      <exclusions>
        <exclusion>
          <groupId>io.swagger</groupId>
          <artifactId>swagger-models</artifactId>
        </exclusion>
        <exclusion>
          <groupId>io.swagger</groupId>
          <artifactId>swagger-annotations</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>io.swagger</groupId>
      <artifactId>swagger-models</artifactId>
      <version>1.6.0</version>
    </dependency>
    <dependency>
      <groupId>io.swagger</groupId>
      <artifactId>swagger-annotations</artifactId>
      <version>1.6.0</version>
    </dependency>
    
    <!-- 不满意可以注释掉换其他UI,可以同时开启多个UI -->
    <!-- 官方UI,请求路径 http://{host}:{port}/swagger-ui.html -->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.9.2</version>
    </dependency>
    
    
    <!-- bootstrap-ui,请求路径:http://{host}:{port}/doc.html,觉得是最好的UI -->
    <!--<dependency>
          <groupId>com.github.xiaoymin</groupId>
          <artifactId>swagger-bootstrap-ui</artifactId>
          <version>1.9.6</version>
    </dependency>-->
    
    
    <!-- ui-layer,请求路径:http://{host}:{port}/docs.html,觉得没比原生的好 -->
    <!--<dependency>
          <groupId>com.github.caspar-chen</groupId>
            <artifactId>swagger-ui-layer</artifactId>
            <version>1.1.3</version>
    </dependency>-->
    <!-- Swagger2 END -->
    
  2. 写一个注解,用于标注

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    public @interface ApiVersion {
        /**
         * 分组名(即版本号),可以传入多个
         *
         * @return
         */
        String[] value();
    }
    
  3. 写一个版本常量类,有新版本就在这里加

    public interface ApiVersionConstant {
        String VERSION_2_7_3 = "v2.7.3";
        String VERSION_2_8 = "v2.8";
    }
    
  4. 写Swagger的配置类

    package com.wyf.test.swagger2springboot.config.swagger;
    
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    import java.lang.reflect.Field;
    import java.util.Arrays;
    
    /**
     * Swagger配置
     *
     * @author Stone
     * @version V1.0.0
     * @date 2020/3/12
     */
    @Configuration
    @EnableSwagger2
    public class Swagger2Config implements InitializingBean {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        // 默认版本的分组
        @Bean
        public Docket docket() {
            // basePackage 需要扫描注解生成文档的路径
            return new Docket(DocumentationType.SWAGGER_2)
                    // 分组名用aaa开头以便排在最前面
                    .groupName("aaa_默认分组(所有接口)")
                    .apiInfo(apiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.wyf.test.swagger2springboot"))
                    .paths(PathSelectors.any())
                    .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("Swagger demo")
                    .description("这是展示Swagger怎么用的例子")
                    .version("1.0").build();
        }
    
        private Docket buildDocket(String groupName) {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .groupName(groupName)
                    .select()
                    .apis(method -> {
                        // 每个方法会进入这里进行判断并归类到不同分组,**请不要调换下面两段代码的顺序,在方法上的注解有优先级**
    
                        // 该方法上标注了版本
                        if (method.isAnnotatedWith(ApiVersion.class)) {
                            ApiVersion apiVersion = method.getHandlerMethod().getMethodAnnotation(ApiVersion.class);
                            if (apiVersion.value() != null && apiVersion.value().length != 0) {
                                if (Arrays.asList(apiVersion.value()).contains(groupName)) {
                                    return true;
                                }
                            }
    
                        }
    
                        // 方法所在的类是否标注了?
                        ApiVersion annotationOnClass = method.getHandlerMethod().getBeanType().getAnnotation(ApiVersion.class);
                        if (annotationOnClass != null) {
                            if (annotationOnClass.value() != null && annotationOnClass.value().length != 0) {
                                if (Arrays.asList(annotationOnClass.value()).contains(groupName)) {
                                    return true;
                                }
                            }
                        }
                        return false;
                    })
                    .paths(PathSelectors.any())
                    .build();
        }
    
        /**
         * 动态得创建Docket bean
         *
         * @throws Exception
         */
        @Override
        public void afterPropertiesSet() throws Exception {
            // ApiConstantVersion 里面定义的每个变量会成为一个docket
            Class<ApiVersionConstant> clazz = ApiVersionConstant.class;
            Field[] declaredFields = clazz.getDeclaredFields();
    
            // 动态注入bean
            AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
            if (autowireCapableBeanFactory instanceof DefaultListableBeanFactory) {
                DefaultListableBeanFactory capableBeanFactory = (DefaultListableBeanFactory) autowireCapableBeanFactory;
                for (Field declaredField : declaredFields) {
    
                    // 要注意 "工厂名和方法名",意思是用这个bean的指定方法创建docket
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition()
                            .setFactoryMethodOnBean("buildDocket", "swagger2Config")
                            .addConstructorArgValue(declaredField.get(ApiVersionConstant.class)).getBeanDefinition();
                    capableBeanFactory.registerBeanDefinition(declaredField.getName(), beanDefinition);
                }
            }
        }
    }
    
  5. 需要放在特别的分组的方法,用@ApiVersion标记,如

    // 放在方法上,可以有多个版本
    @ApiOperation("新增订单")
    @PostMapping("/order/add2")
    @ApiVersion({ApiVersionConstant.VERSION_2_7_3, ApiVersionConstant.VERSION_2_8})
    public ResponseDTO<OrderRespDTO> addOrder2(
      @ApiParam(value = "订单信息", required = true) @Valid OrderReqDTO orderReqDTO
    )
      
      
    // 放在类上,则所有接口都生效
    @RestController
    @Api(tags = "用户模块")
    @ApiVersion(ApiVersionConstant.VERSION_2_7_3)
    public class UserController {
      ...
    }
    
  6. 效果,切换分组仅显示分组所包含的接口

    在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/135276.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!