• 使用方式

    引入依赖

    pub.dsb.framework.boot:dsb-boot-api-starter:0.0.4.RELEASE

    添加 Swagger 注解

    package pub.dsb.api.controller.business;

    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    import pub.dsb.api.object.dto.ProductInfoDTO;
    import pub.dsb.api.object.dto.UserInfoDTO;
    import pub.dsb.framework.boot.rest.R;
    import pub.dsb.framework.boot.security.annotation.Decrypt;

    @Api(value = "/v1/business", tags = {"业务处理模块"})
    @RestController
    @RequestMapping("/v1/business")
    public class BusinessController {


        /**
         * 下单
         *
         * @param productInfo
         * @return
         */

        @RequestMapping(value = "/order", method = RequestMethod.POST)
        @ApiOperation(value = "下单", notes = "下单,返回订单号", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
        public R<String> userInfo(ProductInfoDTO productInfo) {
            return R.ok("business_id_45454545478");
        }
    }
    package pub.dsb.api.object.dto;

    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @ApiModel(description = "商品信息:流水号、信息、数量")
    public class ProductInfoDTO {

        @ApiModelProperty(value = "订单流水号", example = "20200812452404", dataType = "int")
        private int orderNumber;
        @ApiModelProperty(value = "商品名称", example = "Mac 笔记本", dataType = "string")
        private String productName;
        @ApiModelProperty(value = "数量", example = "2", dataType = "int")
        private int count;


    }

    配置扫描规则 application.yaml

    dsb:
      webmvc:
        swagger:
         # 默认分组参数,配置了的话生成默认分组下的接口扫描文档
         # base-package: x.x.x
          # 接口分组集合
          api-groups:
            - group-name: 业务处理模块
              # 接口前缀
              path-mapping: /api/biz/
              # 包路径匹配
              apis: pub.dsb.api.controller.business
              # 路由匹配
              paths: /v1/**
              api-info:
                title: 接口文档标题
                description: 接口文档描述
                terms-of-service-url: 服务地址
                name: 接口负责人:姓名
                url: 接口负责人:个人地址
                email: [email protected]
                license: License 授权
                license-url: License 地址
                version: 接口文档版本号
            - group-name: 基础信息管理
              # 接口前缀
              path-mapping: /api/docs/
              # 包路径匹配
              apis: pub.dsb.api.controller.user
              # 路由匹配
              paths: /v1/**
              api-info:
                title: 接口文档标题
                description: 接口文档描述
                terms-of-service-url: 服务地址
                name: 接口负责人:姓名
                url: 接口负责人:个人地址
                email: [email protected]
                license: License 授权
                license-url: License 地址
                version: 接口文档版本号

    效果

    实现原理

    基于 Spring Boot 的约定大于配置,根据 YAML 中配置的接口分组信息,直接生成对应所需 Docket Bean

    核心源码逻辑

    package pub.dsb.framework.boot.webmvc.configuration;

    import com.google.common.base.Predicate;
    import com.google.common.base.Predicates;
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import pub.dsb.framework.boot.webmvc.configuration.Swagger2Configuration;
    import pub.dsb.framework.boot.webmvc.properties.SwaggerProperties;
    import springfox.documentation.RequestHandler;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.Contact;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
    import springfox.documentation.spring.web.plugins.Docket;

    import java.util.ArrayList;
    import java.util.List;

    /**
     * <p>
     * Swagger API Docket 配置
     * </p>
     *
     * @author Yiyuery
     * @date 2020/8/21
     * @since 0.0.1
     */

    @Configuration
    @Import({Swagger2Configuration.class, SwaggerProperties.class})
    @ConditionalOnProperty(prefix = "dsb.webmvc.swagger", value = "enabled", matchIfMissing = true)
    @ConditionalOnClass(Docket.class)
    public class SwaggerDocketConfiguration implements BeanFactoryAware {

        private BeanFactory beanFactory;

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = beanFactory;
        }

        /**
         * 注册 Docket
         *
         * @param swaggerProperties
         * @return
         */

        @Bean
        public List<Docket> registerDocket(SwaggerProperties swaggerProperties) {
            ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
            List<Docket> beans = new ArrayList();
            List<SwaggerProperties.ApiGroup> apiGroups = swaggerProperties.getApiGroups();
            if (CollectionUtils.isNotEmpty(apiGroups)) {
                registerDockets(apiGroups, beans, configurableBeanFactory);
            } else if (StringUtils.isNotEmpty(swaggerProperties.getBasePackage())) {
                //配置了默认包路径的扫描配置
                Docket docket = new Docket(DocumentationType.SWAGGER_2)
                        .groupName("DEFAULT_GROUP")
                        .select()
                        .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
                        .build();
                beans.add(docket);
                configurableBeanFactory.registerSingleton(docket.getGroupName() + "_Docket", docket);
            }
            return beans;
        }

        /**
         * 解析配置 注册 Docket
         *
         * @param apiGroups
         * @param beans
         * @param configurableBeanFactory
         */

        private void registerDockets(List<SwaggerProperties.ApiGroup> apiGroups, List<Docket> beans, ConfigurableBeanFactory configurableBeanFactory) {

            for (SwaggerProperties.ApiGroup apiGroup : apiGroups) {
                SwaggerProperties.ApiInfo apiInfo = apiGroup.getApiInfo();
                Docket docket = new Docket(DocumentationType.SWAGGER_2)
                        .apiInfo(new ApiInfoBuilder()
                                .contact(new Contact(apiInfo.getName(), apiInfo.getUrl(), apiInfo.getEmail()))
                                .description(apiInfo.getDescription())
                                .title(apiInfo.getTitle())
                                .version(apiInfo.getVersion())
                                .termsOfServiceUrl(apiInfo.getTermsOfServiceUrl())
                                .license(apiInfo.getLicense())
                                .licenseUrl(apiInfo.getLicenseUrl()).build())
                        .groupName(apiGroup.getGroupName());
                ApiSelectorBuilder select = docket.select();
                if (CollectionUtils.isNotEmpty(apiGroup.getApis())) {
                    Predicate<RequestHandler>[] apis = new Predicate[apiGroup.getApis().size()];
                    int index = 0;
                    for (String pack : apiGroup.getApis()) {
                        apis[index++] = RequestHandlerSelectors.basePackage(pack);
                    }
                    select.apis(Predicates.or(apis));
                }
                if (CollectionUtils.isNotEmpty(apiGroup.getPaths())) {
                    Predicate<String>[] paths = new Predicate[apiGroup.getPaths().size()];
                    int index = 0;
                    for (String path : apiGroup.getPaths()) {
                        paths[index++] = PathSelectors.ant(path);
                    }
                    select.paths(Predicates.or(paths));
                }
                
                select.build();
                if (StringUtils.isNotEmpty(apiGroup.getPathMapping())) {
                    docket.pathMapping(apiGroup.getPathMapping());
                }
                beans.add(docket);
                configurableBeanFactory.registerSingleton(docket.getGroupName() + "_Docket", docket);
            }
        }
    }

    小结

    基于 dsb-boot-api-starter,对于接口文档的提供便只剩接口的注解扫描路径等必要的配置工作了,是不是忍不住要赶快试试了呢?

    适用场景:

    接口管理这下总会了吧?-LMLPHP接口管理这下总会了吧?-LMLPHP


    本文分享自微信公众号 - 架构探险之道(zacsnz1314)。
    如有侵权,请联系 [email protected] 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    09-14 14:36