OpenAPI配置类,支持通过@ApiGroup注解自动分组

以下是实现一个支持 @ApiGroup 注解自动分组的 OpenAPI 配置类的详细方法,结合 Spring Boot 和 Swagger 的常见实践。


核心步骤

定义 @ApiGroup 注解 自定义注解用于标记 API 分组,示例代码如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiGroup {
    String value(); // 分组名称
}

配置 OpenAPI 分组自动扫描 利用 GroupedOpenApi 动态生成分组配置,扫描带有 @ApiGroup 的控制器类或方法:


/**
 * OpenAPI配置类,支持通过@ApiGroup注解自动分组
 */
@Configuration
public class OpenApiConfig implements ApplicationContextAware, BeanPostProcessor {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 动态注册 GroupedOpenApi Bean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 延迟初始化,避免循环依赖
        if (bean instanceof RequestMappingHandlerMapping) {
            // 使用Spring事件机制延迟执行
            applicationContext.publishEvent(new OpenApiRegistrationEvent(this, (RequestMappingHandlerMapping) bean));
        }
        return bean;
    }

    /**
     * 自定义事件类,用于延迟注册GroupedOpenApi
     */
    public static class OpenApiRegistrationEvent extends ApplicationEvent {
        private final RequestMappingHandlerMapping mapping;

        public OpenApiRegistrationEvent(Object source, RequestMappingHandlerMapping mapping) {
            super(source);
            this.mapping = mapping;
        }

        public RequestMappingHandlerMapping getMapping() {
            return mapping;
        }
    }

    /**
     * 监听自定义事件,执行GroupedOpenApi注册
     */
    @Bean
    public OpenApiRegistrationEventListener openApiRegistrationEventListener() {
        return new OpenApiRegistrationEventListener();
    }

    public static class OpenApiRegistrationEventListener implements ApplicationListener {
        @Override
        public void onApplicationEvent(OpenApiRegistrationEvent event) {
            OpenApiConfig config = (OpenApiConfig) event.getSource();
            config.registerGroupedOpenApis(event.getMapping());
        }
    }

    /**
     * 注册 GroupedOpenApi Bean
     */
    private void registerGroupedOpenApis(RequestMappingHandlerMapping mapping) {
        Map> groupPaths = scanApiGroups(mapping);
        groupPaths.forEach((groupName, paths) -> {
            if (!paths.isEmpty()) {
                GroupedOpenApi.Builder builder = GroupedOpenApi.builder()
                    .group(groupName)
                    .pathsToMatch(paths.toArray(new String[0]));
                DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
                beanFactory.registerSingleton(groupName, builder.build());
            }
        });
    }

    /**
     * 扫描所有标记了@ApiGroup注解的控制器,并收集它们的路径
     */
    private Map> scanApiGroups(RequestMappingHandlerMapping mapping) {
        Map> groupPaths = new HashMap<>();

        try {
            // 获取所有RequestMapping
            Map handlerMethods = mapping.getHandlerMethods();

            // 遍历所有处理方法
            for (Map.Entry entry : handlerMethods.entrySet()) {
                HandlerMethod handlerMethod = entry.getValue();
                Class controllerClass = handlerMethod.getBeanType();

                // 检查类是否有@RestController和@ApiGroup注解
                if (controllerClass.isAnnotationPresent(RestController.class) &&
                    controllerClass.isAnnotationPresent(ApiGroup.class)) {

                    ApiGroup apiGroup = controllerClass.getAnnotation(ApiGroup.class);
                    // 优先使用group属性,如果为空则使用value属性
                    String groupName = apiGroup.name();

                    // 获取控制器的所有路径
                    Set paths = getControllerPaths(entry.getKey());

                    // 打印调试信息
                    System.out.println("Found group: " + groupName + ", paths: " + paths);

                    // 将路径添加到对应的分组中
                    groupPaths.computeIfAbsent(groupName, k -> new HashSet<>()).addAll(paths);
                } else {
                    // 打印未匹配的控制器信息
                    System.out.println("Controller does not have @ApiGroup annotation: " + controllerClass.getName());
                }
            }
        } catch (Exception e) {
            // 如果出现异常,记录日志
            System.err.println("Error scanning API groups: " + e.getMessage());
            e.printStackTrace();
        }

        // 打印最终的分组路径结果
        System.out.println("Final groupPaths: " + groupPaths);

        return groupPaths;
    }

    /**
     * 获取RequestMappingInfo的所有路径
     */
    private Set getControllerPaths(RequestMappingInfo mappingInfo) {
        Set paths = new HashSet<>();

        // 获取所有路径模式
        if (mappingInfo.getPatternsCondition() != null) {
            mappingInfo.getPatternsCondition().getPatterns().forEach(path -> {
                paths.add(path.getPatternString()+"/**");
            });
        }

        // 打印调试信息
        System.out.println("Extracted paths: " + paths);

        return paths;
    }
}

主 OpenAPI 配置 定义全局 OpenAPI 基本信息:

@Bean
public OpenAPI customOpenAPI() {
    return new OpenAPI()
        .info(new Info()
            .title("API Documentation")
            .version("1.0")
            .description("Automatically grouped APIs"));
}


使用示例

在控制器类或方法上添加 @ApiGroup 注解:

@RestController
@ApiGroup("用户管理")
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/list")
    @ApiGroup("用户查询") // 方法级分组优先
    public List listUsers() {
        return userService.list();
    }
}


关键说明

  1. 优先级规则
    方法上的 @ApiGroup 注解优先于类级别的注解。若均未定义,则不会分配到任何自定义分组。

  2. 路径匹配
    GroupedOpenApi 通过 pathsToMatch 指定组内 API 路径,支持通配符(如 /user/**)。

  3. 依赖要求
    需引入 SpringDoc OpenAPI 库:

    
        org.springdoc
        springdoc-openapi-starter-webmvc-ui
        2.5.0
    
    

  4. 扩展性
    可结合 @ApiOperation 等注解进一步细化分组逻辑,或在 autoGroupedApis() 中过滤特定条件。


通过上述配置,Swagger UI 将自动按 @ApiGroup 定义的名称生成独立的分组页面,便于接口分类管理。

你可能感兴趣的:(OpenAPI配置类,支持通过@ApiGroup注解自动分组)