swagger接口文档分组

解析问题

因项目中的接口越来越多,使用swagger调试接口的时候,因接口太杂太乱,不太容易找到想要调试的接口,所以研究了一下swagger分组的方法

但是分组是需要配置多个Docket的bean,而一般配置,就是一个docket一个函数定义并加上@Bean注解,代码冗余太多,所以就想着用灵活的方式去配置swagger分组。

@Configuration
public class SwaggerConfig {
    /**
     * 创建API 
     */
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30)
                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
                .apiInfo(apiInfo())
                // 设置哪些接口暴露给Swagger展示
                .select()
                // 扫描指定包
                .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
                // 扫描所有 .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .pathMapping(pathMapping)
                .groupName("全部");
    }
    
    /**
     * 添加摘要信息
     */
    private ApiInfo apiInfo() {
        // 用ApiInfoBuilder进行定制
        return new ApiInfoBuilder()
                // 设置标题
                .title("标题:xxx系统_接口文档")
                // 描述
                .description("描述:用于管理xxxxx信息,具体包括XXX,XXX模块...")
                // 作者信息
                .contact(new Contact("xxxx", null, null))
                // 版本
                .version("版本号:" + "1.0")
                .build();
    }
}

就像普通的swagger配置,一个docket包含所有接口, 而这样的话,swagger文档看起来就非常的杂乱,所以分组是个不错的选择

但是一般的分组是多个Docket去配置,比如:

	@Bean
    public Docket createRestApi1() {
        return new Docket(DocumentationType.OAS_30)
            // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
            .apiInfo(apiInfo())
            // 设置哪些接口暴露给Swagger展示
            .select()
            // 扫描指定包
            .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
            // 扫描所有 .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.any())
            .build()
            .pathMapping(pathMapping)
            .groupName("分组1");
    }
	@Bean
    public Docket createRestApi2() {
        return new Docket(DocumentationType.OAS_30)
                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
                .apiInfo(apiInfo())
                // 设置哪些接口暴露给Swagger展示
                .select()
                // 扫描指定包
                .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
                // 扫描所有 .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .pathMapping(pathMapping)
                .groupName("分组2");
    }

	...

一个分组就要复制一个docker定义,如果分组比较多的话,冗余代码太多,而且看起来也不是很美观,所以我才在想如果灵活配置的方式

分析简化方案

首先我想的就是,这里面重复代码这么多,能不能将这些重复代码去掉。

所以我想出了一个简单的方案,就是将不同的定义信息封装成一个定义信息集,而通过循环的方式将不同的信息当作变量放入。

于是我想到了这样一个方式,通过循环定义信息集,动态的去创建Docket并注册到Bean工厂中,但是这样的话,就需要操作spring Bean工厂,于是我又写了一个工具类,去操作spring bean工厂,然后通过这种方式实现了这个想法

实现简化方案

swagger的分组我是通过扫描不同的包来分的组,但是也可以使用url去区分

@Bean
    public Docket createRestApi1() {
        return new Docket(DocumentationType.OAS_30)
            // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
            .apiInfo(apiInfo())
            // 设置哪些接口暴露给Swagger展示
            .select()
            // 扫描所有
            .apis(RequestHandlerSelectors.any())
            // 扫描指定url
            .paths(PathSelectors.ant("/xxx/**"))
            .build()
            .pathMapping(pathMapping)
            .groupName("分组1");
    }

swagger定义信息 我使用的是枚举定义的,当然,也可以使用对象集合这样

package com.we.web.core.config;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import lombok.Getter;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.PathSelectors;

import java.util.function.Predicate;

/**
 * swagger分组
 */
@Getter
public enum SwaggerEntity {

    /** 系统管理 */
    SYSTEM("系统",
            basePackage("com.we.web.controller"),
            PathSelectors.any()),

    /** 后台 */
    OPERATION("后台",
            basePackage("com.we.operation.controller"),
            PathSelectors.ant("/we-api/operation/**")),
    /** 小程序 */
    APPLET("小程序",
            basePackage("com.we.operation.controller.applet", "com.we.operation.controller.wx"),
            PathSelectors.any()),



    ;

    SwaggerEntity(String groupName, Predicate<RequestHandler> apis, Predicate<String> paths) {
        this.groupName = groupName;
        this.apis = apis;
        this.paths = paths;
    }

    /** 分组名称 */
    private final String groupName;
    /** 扫描包 */
    private final Predicate<RequestHandler> apis;
    /** 扫描包 */
    private final Predicate<String> paths;

    private static com.google.common.base.Predicate<RequestHandler> basePackage(String... basePackages) {
        return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }

    private static Function<Class<?>, Boolean> handlerPackage(String[] basePackage)     {
        return input -> {
            // 循环判断匹配
            for (String strPackage : basePackage) {
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

}

basePackages为何定义为数据类型是因为一个Docket可能要扫描多个包,如不需要,则改为String类型就可

下面就开始最关键的注入代码, 因需要项目启动就注入进去,所以使用@PostConstruct来使服务启动完就执行

	/**
     * 注入自定义swagger分组
     *
     * @author liac
     * @date 2022/3/9 14:01
     */
    @PostConstruct
    public void importDocketGroup() {
        // 循环SwaggerEntity中的所有定义
        for (SwaggerEntity value : SwaggerEntity.values()) {
            // 注册bean
            Docket docket = SpringUtils.registerBean(null, Docket.class, 					Lists.newArrayList(DocumentationType.OAS_30), null);
            // 执行docket的一些定义
            docket  // 是否启用Swagger
                    .enable(enabled)
                    // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
                    .apiInfo(apiInfo())
                    // 设置哪些接口暴露给Swagger展示
                    .select()
                    // 设置SwaggerEntity中定义的需要扫描的包
                    .apis(value.getApis())
                    // 扫描所有 .apis(RequestHandlerSelectors.any())
                    .paths(value.getPaths())
                    .build()
                    // 设置分组名
                    .groupName(value.getGroupName())
                    // 设置安全模式,swagger可以设置访问token
                    .securitySchemes(securitySchemes())
                    .securityContexts(securityContexts())
                    .pathMapping(pathMapping);
        }
    }

关于SpringUtils工具类请移步 spring bean工具类

上一篇 下一篇