ImportBeanDefinitionRegistrar的作用
简介
【ImportBeanDefinitionRegistrar的作用】用过Spring
都知道想注入一个Bean很简单!
@Resource
CustomBean customBean;
大概像这样就可以注入一个已经存在容器里的Bean,但前提是你把这个Bean加入容器、加入也很简单,只要在类的上面
@Component
,@Service
等等一些框架提供的注解就可以!现在 我们要自己写一个组件,我们要自己管理我们的组件,使用自己的注解管自己的Bean。
这时候
ImportBeanDefinitionRegistrar
就登场了!基本功 step1 我们也有模有样的起个Enable开头的启动注解、
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ImportCustomBeanDefinitionRegistrar.class)//结合@Import注解,这里参可参考其他Blog
public @interface EnableCustomBean {
}
step2 然后把这个注解放到启动类上,平常都是看大佬们Enable这样用,今个儿我自己也能开发个!
@SpringBootApplication
@EnableCustomBean
public class BootApplication {public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}}
step3 再自定义一个Bean,
public class CustomBean {private String name;
public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}@Override
public String toString() {
return "CustomBean{" +
"name='" + name + '\'' +
'}';
}
}
好了,看起来一切很完美呢,只要把
CustomBean
这个类注册到容器就OK!step4 怎么把
CustomBean
加入容器呢?虽然说我们是自己定义的类,但还是运行在Spring
里,有一些初始工作还是要Spring
帮我们一把。这里实现ImportBeanDefinitionRegistrar
接口,今天的角儿!方法registerBeanDefinitions
在Spring启动的时候会运行到!在博客@Import里有解释。public class ImportCustomBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//到这里就很明白了,
String simpleName = CustomBean.class.getName();
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(CustomBean.class);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("name", "Savey");
rootBeanDefinition.setPropertyValues(propertyValues);
registry.registerBeanDefinition(simpleName, rootBeanDefinition);
}
}
step5
跑个Test看下吧
@SpringBootTest
public class ImportBeanDefinitionRegistrarTest {//只管注入即可,
@Resource
CustomBean bean;
//
@Resource
ApplicationContext applicationContext;
@Test
public void test_register_custom_bean() {
System.out.println(bean);
}
}
不出意外的话打印出
CustomBean{name='Savey'}
,说明我们CustomBean已经被加载到容器里了!进阶 我们要求把某个包下的类都加到容器、另外呢,我还要加一个自定义注解,只有在这个包下加了自定义注解的才能被加到容器!
其实,有了上面的基本功,想实现下面这个功能就太简单多了!
step1 自定义个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Savey {
}
step2 在
EnableCustomBean
注解里加个属性嘛、@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ImportCustomBeanDefinitionRegistrar.class)
public @interface EnableCustomBean {/**
* 定义要扫描的包
*/
String[] basePackages() default {};
}
step3 修改启动类、
@SpringBootApplication
@EnableCustomBean(basePackages = {"com.boot.boot.savey"})
public class BootApplication {public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}}
这里我传递一个basePackages属性,让指定包下的类才加入容器!!
step4 创建
com.boot.boot.savey
目录,在目录下创建一些要自定义Bean,注意: 给类加上
Savey
注解!!@Savey
@Getter
@Setter
@ToString
public class Money {}@Savey
@Getter
@Setter
@ToString
public class Work {}//这个类就不加Savey注解了,不想它加入到容器,不想玩游戏,只想工作!所以不加了!
@Getter
@Setter
@ToString
public class PlayGame {}
step5 修改
ImportCustomBeanDefinitionRegistrar
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取EnableCustoBean注释的属性
final Map attributes = importingClassMetadata.getAnnotationAttributes(EnableCustomBean.class.getName());
//获取包扫描
ClassPathScanningCandidateComponentProvider pathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);
//添加过滤 带有Savey这个注解的类
pathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Savey.class));
LinkedHashSet candidateComponents = new LinkedHashSet<>();
for (String basePackages : (String[]) attributes.get("basePackages")) {
candidateComponents.addAll(pathScanningCandidateComponentProvider.findCandidateComponents(basePackages));
}//注册Bean
for (BeanDefinition candidateComponent : candidateComponents) {
registry.registerBeanDefinition(candidateComponent.getBeanClassName(), candidateComponent);
}
}
打个Test吧
@SpringBootTest
public class ImportCustomScanPackagesDefinitionRegisterTest {@Resource
ApplicationContext applicationContext;
@Test
public void test_scan_packages() {
Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);
}
}
最后只有它俩加入了容器,
PlayGame
却没有!com.boot.boot.savey.Money
com.boot.boot.savey.Work
代码在GitHub