Skip to content

SpringBoot 基于 ImportBeanDefinitionRegistrar 扩展点扫描自定义注解

ImportBeanDefinitionRegistrar 是 Spring 框架提供的一个接口,允许我们在应用启动时动态地注册 Bean。通过实现这个接口,我们可以根据自定义逻辑来扫描和注册 Bean,从而实现更灵活的配置管理。

自定义注解

首先,我们需要定义一个自定义注解 @XSend,用于标记需要扫描的类和接口。

java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface XSend {
}

定义启用注解

接下来,定义一个启用注解 @EnableSend,并使用 @Import 注解导入 XSendBeanDefinitionRegistrar。添加 basePackages 属性,以便指定需要扫描的包。

java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(XSendBeanDefinitionRegistrar.class)
public @interface EnableSend {

    String[] basePackages() default {};

}

实现 ImportBeanDefinitionRegistrar

实现 ImportBeanDefinitionRegistrar 接口,用于在 Spring 容器启动时注册和收集标记了 @XSend 注解的类和接口。

java
@Slf4j
public class XSendBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取 basePackages 属性
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableSend.class.getCanonicalName());
        ArrayList<String> basePackages = new ArrayList<>();
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        // 添加当前项目包
        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }

        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return true; // Include all components
            }
        };
        provider.addIncludeFilter(new AnnotationTypeFilter(XSend.class));

        for (String basePackage : basePackages) {
            Set<BeanDefinition> components = provider.findCandidateComponents(basePackage);
            if (!components.isEmpty()) {
                for (BeanDefinition component : components) {
                    String className = component.getBeanClassName();
                    log.info("找到标记了 XSend 注解的类或接口,名称为:{}", className);
                    // todo 完成你自己的需求, 比如注入容器中, 注入FactoryBean, 等等
                }
            }
        }
    }
}

定义标记注解的类或者接口

java
@XSend
public interface Send1 {

}

@XSend
public class Send2 {

}

启动类

创建一个主类来启动 Spring Boot 应用, 并启用 EnableSend 注解

java
@SpringBootApplication
@EnableSend(basePackages = {"com.ssn.design"})
public class DesignPatternsApplication {
    public static void main(String[] args) {
        SpringApplication.run(DesignPatternsApplication.class,args);
    }
}

控制台可以看到相关的输出

java
2024-10-16 18:33:03.097  INFO 996 --- [           main] c.s.d.e.r.XSendBeanDefinitionRegistrar   : 找到标记了 XSend 注解的类或接口,名称为:com.ssn.design.extend.register.impl.Send1
2024-10-16 18:33:03.098  INFO 996 --- [           main] c.s.d.e.r.XSendBeanDefinitionRegistrar   : 找到标记了 XSend 注解的类或接口,名称为:com.ssn.design.extend.register.impl.Send2

总结

通过使用 ImportBeanDefinitionRegistrar 扩展点,我们可以在 Spring Boot 应用启动时动态地扫描并注册标记了自定义注解的类和接口。这种方法提供了极大的灵活性,使得我们的应用配置更加简洁和强大。

Released under the MIT License.