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 应用启动时动态地扫描并注册标记了自定义注解的类和接口。这种方法提供了极大的灵活性,使得我们的应用配置更加简洁和强大。