eanDefinitionRegistryPostProcessor繼承自BeanFactoryPostProcessor,是一種比較特殊的BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor中定義的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
方法 可以讓我們實(shí)現(xiàn)自定義的注冊(cè)bean定義的邏輯。下面的示例中就新定義了一個(gè)名為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);
}
}
測(cè)試時(shí)采用的配置是基于Java類的配置,對(duì)應(yīng)的配置類如下:
@Configuration
public class SpringConfiguration {
@Bean
public CustomBeanDefinitionRegistry customBeanDefinitionRegistry() {
return new CustomBeanDefinitionRegistry();
}
}
測(cè)試如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={SpringConfiguration.class})
public class CustomBeanDefinitionRegistryTest {
@Autowired
private Hello hello;
@Test
public void test() {
//能運(yùn)行就說明hello是不為空的
Assert.assertNotNull(hello);
}
}
24.1 ClassPathScanningCandidateComponentProvider
在使用自定義的BeanDefinitionRegistryPostProcessor來添加自定義的bean定義時(shí)可以配合ClassPathScanningCandidateComponentProvider一起使用,ClassPathScanningCandidateComponentProvider可以根據(jù)一定的規(guī)則掃描類路徑下滿足特定條件的Class來作為候選的bean定義。 ClassPathScanningCandidateComponentProvider在掃描時(shí)可以通過TypeFilter來指定需要匹配的類和需要排除的類,使用ClassPathScanningCandidateComponentProvider時(shí)可以通過構(gòu)造參數(shù)useDefaultFilter指定是否需要使用默認(rèn)的TypeFilter,默認(rèn)的TypeFilter將包含類上擁有 @Component、@Service、@Repository、@Controller、@javax.annotation.ManagedBean和@javax.inject.Named注解的類。在掃描時(shí)需要指定掃描的根包路徑。以下是一些使用ClassPathScanningCandidateComponentProvider掃描并注冊(cè)bean定義的示例。
24.1.1 掃描指定包及其子包下面的所有非接口和非抽象類。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
boolean useDefaultFilters = false;//是否使用默認(rèn)的filter,使用默認(rèn)的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通常由對(duì)應(yīng)的BeanNameGenerator來生成,比如Spring自帶的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己實(shí)現(xiàn)。
String beanName = beanDefinition.getBeanClassName();
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
24.1.2 掃描指定包及其子包下面擁有指定注解的類。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
boolean useDefaultFilters = false;//是否使用默認(rèn)的filter,使用默認(rèn)的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通常由對(duì)應(yīng)的BeanNameGenerator來生成,比如Spring自帶的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己實(shí)現(xiàn)。
String beanName = beanDefinition.getBeanClassName();
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
AnnotationTypeFilter是Spring自帶的一個(gè)TypeFilter,可以掃描指定的注解。AnnotationTypeFilter一共有三個(gè)構(gòu)造方法,分別如下:
public 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;
}
主要差別在于considerMetaAnnotations和considerInterfaces。
24.1.2.1 considerMetaAnnotations
指定considerMetaAnnotations="true"時(shí)則如果目標(biāo)類上沒有指定的注解,但是目標(biāo)類上的某個(gè)注解上加上了指定的注解則該類也將匹配。比如:
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {
}
@HelloAnnotation
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HasHelloAnnotation {
}
@HasHelloAnnotation
public class Hello {
}
24.1.2.2 considerInterfaces
在上面的代碼中定義了兩個(gè)注解HelloAnnotation和HasHelloAnnotation,其中HasHelloAnnotation上加上了@HelloAnnotation注解,類Hello上面加上了@HasHelloAnnotation注解,則在通過AnnotationTypeFilter掃描標(biāo)注有HelloAnnotation注解的類時(shí),如果指定了considerMetaAnnotations="true"則類Hello也會(huì)被掃描到。
指定considerInterfaces="true"時(shí),則如果對(duì)應(yīng)的類實(shí)現(xiàn)的接口上擁有指定的注解時(shí)也將匹配。比如下面這種情況掃描加了HelloAnnotation注解的類時(shí)就會(huì)掃描到Hello類。
@HelloAnnotation
public interface HelloInterface {
}
public class Hello implements HelloInterface {
}
24.1.2.3 父類上擁有指定的注解
如果我們需要掃描的目標(biāo)注解上是加了@Inherited注解的,則如果一個(gè)類上沒有指定的目標(biāo)注解,但是其父類擁有對(duì)應(yīng)的注解,則也會(huì)被掃描到。比如我們將HelloAnnotation加上@Inherited注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HelloAnnotation {
}
Hello類上加上@HelloAnnotation注解。
@HelloAnnotation
public class Hello {
}
然后HelloChild類繼承自Hello類。
```java
public class HelloChild extends Hello {
}
這時(shí)候進(jìn)行掃描時(shí)HelloChild也會(huì)被掃描到,但如果拿掉HelloAnnotation上的@Inherited,則HelloChild掃描不到。
24.1.3 掃描指定包及其子包下面能賦值給指定Class的Class
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
boolean useDefaultFilters = false;//是否使用默認(rèn)的filter,使用默認(rèn)的filter意味著只掃描那些類上擁有Component、Service、Repository或Controller注解的類。
String basePackage = "com.elim.learn.spring.bean";
ClassPathScanningCandidateComponentProvider beanScanner = new ClassPathScanningCandidateComponentProvider(useDefaultFilters);
//指定considerMetaAnnotations="true"時(shí)則如果目標(biāo)類上沒有指定的注解,但是目標(biāo)類上的某個(gè)注解上加上了指定的注解則該類也將匹配。比如:
TypeFilter includeFilter = new AssignableTypeFilter(Hello.class);
beanScanner.addIncludeFilter(includeFilter);
Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(basePackage);
for (BeanDefinition beanDefinition : beanDefinitions) {
//beanName通常由對(duì)應(yīng)的BeanNameGenerator來生成,比如Spring自帶的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己實(shí)現(xiàn)。
String beanName = beanDefinition.getBeanClassName();
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
AssignableTypeFilter也是Spring內(nèi)置的一個(gè)TypeFilter,用于掃描指定類型的類。只要目標(biāo)類型能夠賦值給指定的類型,則表示匹配。即如果指定的是一個(gè)接口,則所有直接或間接實(shí)現(xiàn)該接口的類都將被掃描到。
基于ClassPathScanningCandidateComponentProvider的特性,我們常常可以利用它構(gòu)建一個(gè)工具類用以掃描指定包路徑下指定類型的Class,獲取滿足條件的Class,然后加以利用,這常用于需要掃描的類不是Spring bean的場(chǎng)景。