首页 > 其他分享 >Springboot3基于SpringDoc实现接口分组功能

Springboot3基于SpringDoc实现接口分组功能

时间:2024-03-26 11:45:15浏览次数:32  
标签:group springdoc custom 分组 Springboot3 SpringDoc configs public elt

问题

最近在接触SpringBoot3,整合Swagger文档组件的过程中发现一个问题,通过springdoc.group-configs[0].display-name=示例接口配置分组名称在界面上面还是显示OpenAPI definition,如下图所示:

查询了下官方文档,可以通过注入GroupedOpenApi对象实现接口分组描述信息展示,同时找到了一个issues提供了一种硬编码的实现方式,但是硬编码的实现方式不利于后期接口描述信息的问题,理想方式还是在配置文件中进行维护,随后查阅了SpringDoc底层的AutoConfiguration实现逻辑,并没有找到相关配置项,所以开始动手改造,增加相关的配置项,然后动态注入GroupedOpenApi

解决思路

自定义GroupConfig配置类:

public class CustomerGroupConfig extends SpringDocConfigProperties.GroupConfig {

    private Info apiInfo;

    public CustomerGroupConfig() {
    }

    public CustomerGroupConfig(String group, List<String> pathsToMatch, List<String> packagesToScan, List<String> packagesToExclude, List<String> pathsToExclude, List<String> producesToMatch, List<String> consumesToMatch, List<String> headersToMatch, String displayName, Info apiInfo) {
        super(group, pathsToMatch, packagesToScan, packagesToExclude, pathsToExclude, producesToMatch, consumesToMatch, headersToMatch, displayName);
        this.apiInfo = apiInfo;
    }

    public Info getApiInfo() {
        return apiInfo;
    }

    public CustomerGroupConfig setApiInfo(Info apiInfo) {
        this.apiInfo = apiInfo;
        return this;
    }
}
public class CustomSpringDocConfigProperties {

    private Set<CustomerGroupConfig> groupConfigs = new HashSet();

    public CustomSpringDocConfigProperties() {
    }

    public CustomSpringDocConfigProperties(Set<CustomerGroupConfig> groupConfigs) {
        this.groupConfigs = groupConfigs;
    }

    public Set<CustomerGroupConfig> getGroupConfigs() {
        return groupConfigs;
    }

    public Set<SpringDocConfigProperties.GroupConfig> getOriginGroupConfigs() {
        return groupConfigs.stream().map(this::convert).collect(Collectors.toSet());
    }

    public SpringDocConfigProperties.GroupConfig convert(CustomerGroupConfig config) {
        SpringDocConfigProperties.GroupConfig groupConfig = new SpringDocConfigProperties.GroupConfig();
        groupConfig.setGroup(config.getGroup());
        groupConfig.setDisplayName(config.getDisplayName());
        groupConfig.setPackagesToScan(config.getPackagesToScan());
        return groupConfig;
    }

}

通过BeanDefinitionRegistryPostProcessor向Spring容器中动态注入GroupedOpenApi接口分组对象,部分代码参考了源码类SpringdocBeanFactoryConfigurer

@Configuration
@ConditionalOnProperty(
        name = {"springdoc.api-docs.enabled"},
        matchIfMissing = true
)
@ConditionalOnWebApplication
public class DynamicSpringDocGroupConfiguration implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    private static final String CONFIG_PRO_PREFIX = "springdoc.custom";

    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        final DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;

        final BindResult<CustomSpringDocConfigProperties> result = Binder.get(environment)
                .bind(CONFIG_PRO_PREFIX, CustomSpringDocConfigProperties.class);

        if (result.isBound()) {
            List<GroupedOpenApi> groupedOpenApis = result.get().getGroupConfigs().stream()
                    .map(elt -> {
                        GroupedOpenApi.Builder builder = GroupedOpenApi.builder();
                        if (!CollectionUtils.isEmpty(elt.getPackagesToScan()))
                            builder.packagesToScan(elt.getPackagesToScan().toArray(new String[0]));
                        if (!CollectionUtils.isEmpty(elt.getPathsToMatch()))
                            builder.pathsToMatch(elt.getPathsToMatch().toArray(new String[0]));
                        if (!CollectionUtils.isEmpty(elt.getProducesToMatch()))
                            builder.producesToMatch(elt.getProducesToMatch().toArray(new String[0]));
                        if (!CollectionUtils.isEmpty(elt.getConsumesToMatch()))
                            builder.consumesToMatch(elt.getConsumesToMatch().toArray(new String[0]));
                        if (!CollectionUtils.isEmpty(elt.getHeadersToMatch()))
                            builder.headersToMatch(elt.getHeadersToMatch().toArray(new String[0]));
                        if (!CollectionUtils.isEmpty(elt.getPathsToExclude()))
                            builder.pathsToExclude(elt.getPathsToExclude().toArray(new String[0]));
                        if (!CollectionUtils.isEmpty(elt.getPackagesToExclude()))
                            builder.packagesToExclude(elt.getPackagesToExclude().toArray(new String[0]));
                        if (StringUtils.isNotEmpty(elt.getDisplayName()))
                            builder.displayName(elt.getDisplayName());

                        if (Optional.ofNullable(elt.getApiInfo()).isPresent()) {
                            builder.addOpenApiCustomizer(openApi -> openApi.info(elt.getApiInfo()));
                        }

                        return builder
                                .group(elt.getGroup())
                                .build();
                    })
                    .toList();


            groupedOpenApis.forEach(elt -> {
                BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(GroupedOpenApi.class, () -> elt).getBeanDefinition();
                defaultListableBeanFactory.registerBeanDefinition(String.format("%sGroupedOpenApi", elt.getGroup()), beanDefinition);
            });

        }

    }

}

增加配置项,用于@Conditional(CacheOrGroupedOpenApiCondition.class)条件成立,注入MultipleOpenApiSupportConfiguration等配置类:

springdoc.show-actuator=true

接口分组配置如下:

springdoc.custom.group-configs[0].group=demo
springdoc.custom.group-configs[0].display-name=示例接口
springdoc.custom.group-configs[0].packages-to-scan[0]=cn.codest.server.common
springdoc.custom.group-configs[0].api-info.title=示例接口
springdoc.custom.group-configs[0].api-info.description=示例接口清单
springdoc.custom.group-configs[0].api-info.version=v1.0
springdoc.custom.group-configs[0].api-info.termsOfService=http://localhost:8080/
springdoc.custom.group-configs[0].api-info.license.name=Apache 2.0
springdoc.custom.group-configs[1].group=user
springdoc.custom.group-configs[1].display-name=用户管理接口
springdoc.custom.group-configs[1].packages-to-scan[0]=cn.codest.server.user
springdoc.custom.group-configs[1].api-info.title=用户管理
springdoc.custom.group-configs[1].api-info.description=用户管理接口清单
springdoc.custom.group-configs[1].api-info.version=v1.0
springdoc.custom.group-configs[1].api-info.termsOfService=http://localhost:8080/
springdoc.custom.group-configs[1].api-info.license.name=Apache 2.0

效果如下:

后面有空再GitHub提交一个PR给SpringDoc。

标签:group,springdoc,custom,分组,Springboot3,SpringDoc,configs,public,elt
From: https://www.cnblogs.com/changxy-codest/p/18096294

相关文章

  • SpringBoot3项目使用Knife4j时访问doc.html出现Knife4j文档请求异常且开发者工具网络
    1.在各个pom.xml中替换Knife4j的依赖版本,升级为4.0以上,如果找不到依赖可以在Maven配置中多添加几个镜像,或者使用汉化插件重启IDEA;<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId......
  • 排序算法练习——按照字符串的异位词分组:给定一个字符串数组,将所有异位词(字符相同但顺
    按照字符串的异位词分组:给定一个字符串数组,将所有异位词(字符相同但顺序不同的字符串)分组到同一个组中。要按照字符串的异位词分组,可以使用哈希表来将每个字符串排序后作为键,相同键的字符串即为异位词。以下是实现这个算法的Python代码:fromcollectionsimportdefaultdict......
  • 华为OD机试C++ - 游戏分组
    游戏分组前言:本专栏将持续更新互联网大厂机试真题,并进行详细的分析与解答,包含完整的代码实现,希望可以帮助到正在努力的你。关于大厂机试流程、面经、面试指导等,如有任何疑问,欢迎联系我,wechat:steven_moda;email:nansun0903@163.com;备注:CSDN。题目描述部门准备举办一场王者......
  • SpringBoot3集成PostgreSQL
    标签:PostgreSQL.Druid.Mybatis.Plus;一、简介PostgreSQL是一个功能强大的开源数据库系统,具有可靠性、稳定性、数据一致性等特点,且可以运行在所有主流操作系统上,包括Linux、Unix、Windows等。通过官方文档可以找到大量描述如何安装和使用PostgreSQL的信息。环境搭建,基于Centos......
  • PointNet++论文复现(二)【最远点采样-球查询-采样和分组 代码详解】
    最远点采样-球查询-采样和分组-代码详解专栏持续更新中!关注博主查看后续部分!最远点采样、球查询等位于pointnet2_utils.py定义点云坐标归一化点云坐标归一化是一种预处理步骤,用于将点云数据标准化到一个统一的尺度,通常是在一个特定的范围内,比如[-1,1]或[0,1]。这一......
  • 关于简单序列分组的基本思路
    描述:题目是很简单的题目,大家应该都能秒了,只是也许大家都没有认真琢磨过这个简单的算法是怎么通过逻辑推理出来的,我看了网上很多大佬的解释都是直接给结论,这并不利于我们逻辑思维的成长,记忆并不是懂得。不才力求谁看谁明白。问题:现有一副扑克牌,需要按顺序轮流发给三个玩家,编......
  • Collections工具类,可以使用collections工具类对代码中的list进行分组
    /***根据活动id进行分组*key活动id*value活动id对应的商品id*/Map<Long,Set<Long>>collect=activitySkuList.stream().collect(Collectors.groupingBy(ActivitySku::getActivityId......
  • 力扣HOT100 - 49. 字母异位词分组
    解题思路:排序注意:返回时不能用List,因为List是抽象类,return的必须是List的具体实现,如ArrayListclassSolution{publicList<List<String>>groupAnagrams(String[]strs){Map<String,List<String>>map=newHashMap<>();for(Stringstr......
  • 应用密码学——分组密码
    DES算法描述明文分组为64位,初始密钥64位,有效密钥56位,输出密文64位,16轮迭代的分组对称密码算法。由置换、替换、异或、循环移位组成。流程图加密过程密钥生成64位初始密钥先进行一个PC-1置换,目的是根据置换表去掉8位奇偶校验位,并打乱剩下的56位有效密钥的顺序。将这56位分......
  • SpringBoot3.x与SpringDoc OpenApi之Swagger接口排序
    直接使用Swagger之后,发现所有的Controller接口菜单都是无序的先看一下效果 就是利用了一下SpringDoc提供的接口做了一下自定义排序1.在Controller上加上注解@Tag(name="MenuController",description="1-菜单管理")这里需要注意description属性,在下面的代码里......