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

    推荐阅读