【spring源碼分析】BeanDefinitionRegistryPostProcessor解析


一、自定義BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor,是一種比較特殊的BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor中定義的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法 可以讓我們實現自定義的注冊bean定義的邏輯。下面的示例中就新定義了一個名為hello,類型為Hello的bean定義。

public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition helloBean = new RootBeanDefinition(Hello.class);
        //新增Bean定義
        registry.registerBeanDefinition("hello", helloBean);
    }

}
View Code

 

 測試時采用的配置是基於Java類的配置,對應的配置類如下:
@Configuration
public class SpringConfiguration {

    @Bean
    public CustomBeanDefinitionRegistry customBeanDefinitionRegistry() {
        return new CustomBeanDefinitionRegistry();
    }
    
}
View Code

測試如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={SpringConfiguration.class})
public class CustomBeanDefinitionRegistryTest {

    @Autowired
    private Hello hello;
    
    @Test
    public void test() {
        //能運行就說明hello是不為空的
        Assert.assertNotNull(hello);
    }
    
}
View Code

二、ClassPathScanningCandidateComponentProvider

在使用自定義的BeanDefinitionRegistryPostProcessor來添加自定義的bean定義時可以配合ClassPathScanningCandidateComponentProvider一起使用,ClassPathScanningCandidateComponentProvider可以根據一定的規則掃描類路徑下滿足特定條件的Class來作為候選的bean定義。 ClassPathScanningCandidateComponentProvider在掃描時可以通過TypeFilter來指定需要匹配的類和需要排除的類,使用ClassPathScanningCandidateComponentProvider時可以通過構造參數useDefaultFilter指定是否需要使用默認的TypeFilter,默認的TypeFilter將包含類上擁有 @Component、@Service、@Repository、@Controller、@javax.annotation.ManagedBean和@javax.inject.Named注解的類。在掃描時需要指定掃描的根包路徑。以下是一些使用ClassPathScanningCandidateComponentProvider掃描並注冊bean定義的示例。

掃描指定包及其子包下面的所有非接口和非抽象類。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    boolean useDefaultFilters = false;//是否使用默認的filter,使用默認的filter意味着只掃描那些類上擁有Component、Service、Repository或Controller注解的類。
    String basePackage = "com.elim.learn.spring.bean";
    ClassPathScanningCandidateComponentProvider beanScanner = new ClassPathScanningCandidateComponentProvider(useDefaultFilters);
    TypeFilter includeFilter = new TypeFilter() {

        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
                throws IOException {
            return metadataReader.getClassMetadata().isConcrete();
        }
        
    };
    beanScanner.addIncludeFilter(includeFilter);
    Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(basePackage);
    for (BeanDefinition beanDefinition : beanDefinitions) {
        //beanName通常由對應的BeanNameGenerator來生成,比如Spring自帶的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己實現。
        String beanName = beanDefinition.getBeanClassName();
        registry.registerBeanDefinition(beanName, beanDefinition);
    }
}
View Code

掃描指定包及其子包下面擁有指定注解的類。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    boolean useDefaultFilters = false;//是否使用默認的filter,使用默認的filter意味着只掃描那些類上擁有Component、Service、Repository或Controller注解的類。
    String basePackage = "com.elim.learn.spring.bean";
    ClassPathScanningCandidateComponentProvider beanScanner = new ClassPathScanningCandidateComponentProvider(useDefaultFilters);
    TypeFilter includeFilter = new AnnotationTypeFilter(HelloAnnotation.class);
    beanScanner.addIncludeFilter(includeFilter);
    Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(basePackage);
    for (BeanDefinition beanDefinition : beanDefinitions) {
        //beanName通常由對應的BeanNameGenerator來生成,比如Spring自帶的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己實現。
        String beanName = beanDefinition.getBeanClassName();
        registry.registerBeanDefinition(beanName, beanDefinition);
    }
}
View Code

AnnotationTypeFilter是Spring自帶的一個TypeFilter,可以掃描指定的注解。AnnotationTypeFilter一共有三個構造方法,分別如下:

ublic AnnotationTypeFilter(Class<? extends Annotation> annotationType) {
    this(annotationType, true, false);
}

public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations) {
    this(annotationType, considerMetaAnnotations, false);
}

public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) {
    super(annotationType.isAnnotationPresent(Inherited.class), considerInterfaces);
    this.annotationType = annotationType;
    this.considerMetaAnnotations = considerMetaAnnotations;
}
View Code

主要差別在於considerMetaAnnotations和considerInterfaces。

considerMetaAnnotations

指定considerMetaAnnotations="true"時則如果目標類上沒有指定的注解,但是目標類上的某個注解上加上了指定的注解則該類也將匹配。比如:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {

}
@HelloAnnotation
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HasHelloAnnotation {

}
@HasHelloAnnotation
public class Hello {

}
View Code

considerInterfaces

在上面的代碼中定義了兩個注解HelloAnnotation和HasHelloAnnotation,其中HasHelloAnnotation上加上了@HelloAnnotation注解,類Hello上面加上了@HasHelloAnnotation注解,則在通過AnnotationTypeFilter掃描標注有HelloAnnotation注解的類時,如果指定了considerMetaAnnotations="true"則類Hello也會被掃描到。
指定considerInterfaces="true"時,則如果對應的類實現的接口上擁有指定的注解時也將匹配。比如下面這種情況掃描加了HelloAnnotation注解的類時就會掃描到Hello類。

@HelloAnnotation
public interface HelloInterface {

}
public class Hello implements HelloInterface {

}
View Code

父類上擁有指定的注解

如果我們需要掃描的目標注解上是加了@Inherited注解的,則如果一個類上沒有指定的目標注解,但是其父類擁有對應的注解,則也會被掃描到。比如我們將HelloAnnotation加上@Inherited注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HelloAnnotation {

}
View Code

Hello類上加上@HelloAnnotation注解。

@HelloAnnotation
public class Hello {

}
然后HelloChild類繼承自Hello類。
```java
public class HelloChild extends Hello {

}
View Code

這時候進行掃描時HelloChild也會被掃描到,但如果拿掉HelloAnnotation上的@Inherited,則HelloChild掃描不到。

掃描指定包及其子包下面能賦值給指定Class的Class

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    boolean useDefaultFilters = false;//是否使用默認的filter,使用默認的filter意味着只掃描那些類上擁有Component、Service、Repository或Controller注解的類。
    String basePackage = "com.elim.learn.spring.bean";
    ClassPathScanningCandidateComponentProvider beanScanner = new ClassPathScanningCandidateComponentProvider(useDefaultFilters);
    //指定considerMetaAnnotations="true"時則如果目標類上沒有指定的注解,但是目標類上的某個注解上加上了指定的注解則該類也將匹配。比如:
    TypeFilter includeFilter = new AssignableTypeFilter(Hello.class);
    beanScanner.addIncludeFilter(includeFilter);
    Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(basePackage);
    for (BeanDefinition beanDefinition : beanDefinitions) {
        //beanName通常由對應的BeanNameGenerator來生成,比如Spring自帶的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己實現。
        String beanName = beanDefinition.getBeanClassName();
        registry.registerBeanDefinition(beanName, beanDefinition);
    }
}
View Code

AssignableTypeFilter也是Spring內置的一個TypeFilter,用於掃描指定類型的類。只要目標類型能夠賦值給指定的類型,則表示匹配。即如果指定的是一個接口,則所有直接或間接實現該接口的類都將被掃描到。

基於ClassPathScanningCandidateComponentProvider的特性,我們常常可以利用它構建一個工具類用以掃描指定包路徑下指定類型的Class,獲取滿足條件的Class,然后加以利用,這常用於需要掃描的類不是Spring bean的場景。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM