Spring(24)——自定义BeanDefinitionRegistryPostProcessor

简介: 24 自定义BeanDefinitionRegistryPostProcessor BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,是一种比较特殊的BeanFactoryPostProcessor。

24 自定义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);
	}

}

测试时采用的配置是基于Java类的配置,对应的配置类如下:

@Configuration
public class SpringConfiguration {

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

测试如下:

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

	@Autowired
	private Hello hello;
	
	@Test
	public void test() {
		//能运行就说明hello是不为空的
		Assert.assertNotNull(hello);
	}
	
}

24.1 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定义的示例。

24.1.1 扫描指定包及其子包下面的所有非接口和非抽象类。

@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);
	}
}

24.1.2 扫描指定包及其子包下面拥有指定注解的类。

@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);
	}
}

AnnotationTypeFilter是Spring自带的一个TypeFilter,可以扫描指定的注解。AnnotationTypeFilter一共有三个构造方法,分别如下:

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"时则如果目标类上没有指定的注解,但是目标类上的某个注解上加上了指定的注解则该类也将匹配。比如:

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

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

}
@HasHelloAnnotation
public class Hello {

}

24.1.2.2 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 {

}

24.1.2.3 父类上拥有指定的注解

如果我们需要扫描的目标注解上是加了@Inherited注解的,则如果一个类上没有指定的目标注解,但是其父类拥有对应的注解,则也会被扫描到。比如我们将HelloAnnotation加上@Inherited注解。

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

}

Hello类上加上@HelloAnnotation注解。

@HelloAnnotation
public class Hello {

}
然后HelloChild类继承自Hello类。
```java
public class HelloChild extends Hello {

}

这时候进行扫描时HelloChild也会被扫描到,但如果拿掉HelloAnnotation上的@Inherited,则HelloChild扫描不到。

24.1.3 扫描指定包及其子包下面能赋值给指定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);
	}
}

AssignableTypeFilter也是Spring内置的一个TypeFilter,用于扫描指定类型的类。只要目标类型能够赋值给指定的类型,则表示匹配。即如果指定的是一个接口,则所有直接或间接实现该接口的类都将被扫描到。

基于ClassPathScanningCandidateComponentProvider的特性,我们常常可以利用它构建一个工具类用以扫描指定包路径下指定类型的Class,获取满足条件的Class,然后加以利用,这常用于需要扫描的类不是Spring bean的场景。

(注:本文是基于Spring4.1.0所写)

目录
相关文章
|
3天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
37 14
|
23天前
|
安全 Java 应用服务中间件
如何将Spring Boot应用程序运行到自定义端口
如何将Spring Boot应用程序运行到自定义端口
34 0
|
4月前
|
Java 数据安全/隐私保护 Spring
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
|
4月前
|
JSON 安全 Java
|
4月前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
230 0
|
5月前
|
消息中间件 Java Kafka
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
213 5
|
5月前
|
Java Spring 容器
Spring boot 自定义ThreadPoolTaskExecutor 线程池并进行异步操作
Spring boot 自定义ThreadPoolTaskExecutor 线程池并进行异步操作
254 3
|
4月前
|
存储 Java API
|
4月前
|
安全 搜索推荐 Java
|
4月前
|
Java Spring
Spring Boot Admin 自定义健康检查
Spring Boot Admin 自定义健康检查
50 0
下一篇
DataWorks