Elim的博客

用来记录一些原创性的总结


Elim

ImportBeanDefinitionRegistrar介绍

在上一篇博文http://elim.iteye.com/blog/2428994中介绍了ImportSelector的作用及其用法。本文需要介绍的ImportBeanDefinitionRegistrar的用法和作用跟ImportSelector类似。唯一的不同点是ImportBeanDefinitionRegistrar的接口方法void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)的返回类型是void,且多了一个BeanDefinitionRegistry类型的参数,它允许我们直接通过BeanDefinitionRegistry对象注册bean。承接上文内容,为了扫描并注册HelloService类型的bean,我们可以自定义如下ImportBeanDefinitionRegistrar实现类。在实现类中可以使用ClassPathBeanDefinitionScanner进行扫描并自动注册,它是ClassPathScanningCandidateComponentProvider的子类,所以还是可以添加相同的TypeFilter,然后通过scanner.scan(basePackages)扫描指定的basePackage下满足条件的Class并注册它们为bean。

public class HelloImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
        String[] basePackages = (String[]) annotationAttributes.get("basePackages");
        
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
        TypeFilter helloServiceFilter = new AssignableTypeFilter(HelloService.class);
        scanner.addIncludeFilter(helloServiceFilter);
        scanner.scan(basePackages);
    }

}

此时我们的@Configuration配置类可以进行如下定义。

@Configuration
@ComponentScan("com.elim.spring.core.importselector")
@Import(HelloImportBeanDefinitionRegistrar.class)
public class HelloConfiguration {

}

为它定义一个特定的注解也是可以的,比如下面代码为HelloImportBeanDefinitionRegistrar定义了@HelloScan,其value属性和basePackages属性互为别名,用于指定需要扫描的basePackage。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(HelloImportBeanDefinitionRegistrar.class)
public @interface HelloScan {

    @AliasFor("value")
    String[] basePackages() default {};
    
    @AliasFor("basePackages")
    String[] value() default {};
    
}

为了满足@HelloScan指定扫描的basePackage的需求,我们的HelloImportBeanDefinitionRegistrar需要改造为如下这样。

public class HelloImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(HelloScan.class.getName());
        String[] basePackages = (String[]) annotationAttributes.get("basePackages");
        if (basePackages == null || basePackages.length == 0) {//HelloScan的basePackages默认为空数组
            String basePackage = null;
            try {
                basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            basePackages = new String[] {basePackage};
        }
        
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
        TypeFilter helloServiceFilter = new AssignableTypeFilter(HelloService.class);
        scanner.addIncludeFilter(helloServiceFilter);
        scanner.scan(basePackages);
    }

}

此时我们的HelloConfiguration可以定义为如下这样,它的效果和之前是一模一样的。

@Configuration
@HelloScan("com.elim.spring.core.importselector")
public class HelloConfiguration {

}

(注:本文是基于Spring 5.0.7所写)