乐趣区

关于java: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 im  portingClassMetadata, 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<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableCustomBean.class.getName());

    // 获取包扫描
    ClassPathScanningCandidateComponentProvider pathScanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);

    // 增加过滤 带有 Savey 这个注解的类
    pathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Savey.class));

    LinkedHashSet<BeanDefinition> 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

退出移动版