spring成神之路第二十九篇:BeanFactory 扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)
Spring中有2个非常重要的接口:BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,这2个接口面试中也会经常问到,本文我们一起来拿下他们俩。
先来看几个问题
-
BeanFactoryPostProcessor是做什么的?
-
BeanDefinitionRegistryPostProcessor是干什么的?
-
BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor有什么区别?
-
这几个接口的执行顺序是什么样的?
Spring容器中主要的4个阶段
-
阶段1:Bean注册阶段,此阶段会完成所有bean的注册
-
阶段2:BeanFactory后置处理阶段
-
阶段3:注册BeanPostProcessor
-
阶段4:bean创建阶段,此阶段完成所有单例bean的注册和装载操作,这个阶段不是我们本文关注的重点,有兴趣的,可以去看之前的文章中有详细介绍:Bean生命周期详解
本文介绍的2个接口主要和前2个阶段有关系,下面我们主要来看前2个阶段。
阶段1:Bean注册阶段
概述
spring中所有bean的注册都会在此阶段完成,按照规范,所有bean的注册必须在此阶段进行,其他阶段不要再进行bean的注册。
这个阶段spring为我们提供1个接口:BeanDefinitionRegistryPostProcessor,spring容器在这个阶段中会获取容器中所有类型为BeanDefinitionRegistryPostProcessor
的bean,然后会调用他们的postProcessBeanDefinitionRegistry
方法,源码如下,方法参数类型是BeanDefinitionRegistry
,这个类型大家都比较熟悉,即bean定义注册器,内部提供了一些方法可以用来向容器中注册bean。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
这个接口还继承了BeanFactoryPostProcessor
接口,这个大家先不用关心,一会阶段2中会介绍。
当容器中有多个BeanDefinitionRegistryPostProcessor
的时候,可以通过下面任意一种方式来指定顺序
-
实现
org.springframework.core.PriorityOrdered
接口 -
实现
org.springframework.core.Ordered
接口
执行顺序:
PriorityOrdered.getOrder() asc,Ordered.getOrder() asc
下面通过案例来感受一下效果。
案例1:简单实用
此案例演示BeanDefinitionRegistryPostProcessor
的简单使用
自定义一个BeanDefinitionRegistryPostProcessor
下面我们定义了一个类,需要实现BeanDefinitionRegistryPostProcessor
接口,然后会让我们实现2个方法,大家重点关注postProcessBeanDefinitionRegistry
这个方法,另外一个方法来自于BeanFactoryPostProcessor
,一会我们后面在介绍这个方法,在postProcessBeanDefinitionRegistry
方法中,我们定义了一个bean,然后通过registry
将其注册到容器了,代码很简单
package com.javacode2018.lesson003.demo3.test0;
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//定义一个字符串类型的bean
AbstractBeanDefinition userNameBdf = BeanDefinitionBuilder.
genericBeanDefinition(String.class).
addConstructorArgValue("路人").
getBeanDefinition();
//将userNameBdf注册到spring容器中
registry.registerBeanDefinition("userName", userNameBdf);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
同包中来个配置类
package com.javacode2018.lesson003.demo3.test0;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class MainConfig0 {
}
测试用例
package com.javacode2018.lesson003.demo3;
import com.javacode2018.lesson003.demo3.test0.MainConfig0;
import com.javacode2018.lesson003.demo3.test1.MainConfig1;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class BeanDefinitionRegistryPostProcessorTest {
@Test
public void test0() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig0.class);
context.refresh();
System.out.println(context.getBean("userName"));
}
}
运行输出
路人
案例2:多个指定顺序
下面我们定义2个BeanDefinitionRegistryPostProcessor
,都实现Ordered
接口,第一个order的值为2,第二个order的值为1,我们来看一下具体执行的顺序。
第一个
package com.javacode2018.lesson003.demo3.test1;
@Component
public class BeanDefinitionRegistryPostProcessor1 implements BeanDefinitionRegistryPostProcessor, Ordered {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println(String.format("BeanDefinitionRegistryPostProcessor1{order=%d},注册name bean,", this.getOrder()));
//定义一个bean
AbstractBeanDefinition nameBdf = BeanDefinitionBuilder.
genericBeanDefinition(String.class).
addConstructorArgValue("路人甲java").
getBeanDefinition();
//将定义的bean注册到容器
registry.registerBeanDefinition("name", nameBdf);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public int getOrder() {
return 2;
}
}
第二个
package com.javacode2018.lesson003.demo3.test1;
@Component
public class BeanDefinitionRegistryPostProcessor2 implements BeanDefinitionRegistryPostProcessor, Ordered {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println(String.format("BeanDefinitionRegistryPostProcessor2{order=%d},注册car bean,", this.getOrder()));
//定义一个bean
AbstractBeanDefinition nameBdf = BeanDefinitionBuilder.
genericBeanDefinition(String.class).
addConstructorArgValue("保时捷").
getBeanDefinition();
//将定义的bean注册到容器
registry.registerBeanDefinition("car", nameBdf);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public int getOrder() {
return 1;
}
}
上面2个类中的postProcessBeanDefinitionRegistry方法第一行都有输出,一个可以通过运行结果看到执行的顺序。
同包中添加配置类
package com.javacode2018.lesson003.demo3.test1;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class MainConfig1 {
}
测试案例
@Test
public void test1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig1.class);
context.refresh();
context.getBeansOfType(String.class).forEach((beanName, bean) -> {
System.out.println(String.format("%s->%s", beanName, bean));
});
}
运行输出
BeanDefinitionRegistryPostProcessor2{order=1},注册car bean,
BeanDefinitionRegistryPostProcessor1{order=2},注册name bean,
car->保时捷
name->路人甲java
小结
BeanDefinitionRegistryPostProcessor
有个非常重要的实现类:
org.springframework.context.annotation.ConfigurationClassPostProcessor
这个类可能有些人不熟悉,下面这些注解大家应该比较熟悉吧,这些注解都是在上面这个类中实现的,通过这些注解来实现bean的批量注册
@Configuration
@ComponentScan
@Import
@ImportResource
@PropertySource
有兴趣的朋友可以去看一下ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
研究一下上面这些注解的解析过程,可以学到很多东西。
阶段2:BeanFactory后置处理阶段
概述
到这个阶段的时候,spring容器已经完成了所有bean的注册,这个阶段中你可以对BeanFactory中的一些信息进行修改,比如修改阶段1中一些bean的定义信息,修改BeanFactory的一些配置等等,此阶段spring也提供了一个接口来进行扩展:BeanFactoryPostProcessor
,简称bfpp
,接口中有个方法postProcessBeanFactory
,spring会获取容器中所有BeanFactoryPostProcessor类型的bean,然后调用他们的postProcessBeanFactory
,来看一下这个接口的源码:
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
当容器中有多个BeanFactoryPostProcessor
的时候,可以通过下面任意一种方式来指定顺序
-
实现
org.springframework.core.PriorityOrdered
接口 -
实现
org.springframework.core.Ordered
接口
执行顺序:
PriorityOrdered.getOrder() asc,Ordered.getOrder() asc
下面通过案例来感受一下效果。
案例
这个案例中演示,在BeanFactoryPostProcessor来修改bean中已经注册的bean定义的信息,给一个bean属性设置一个值。
先来定义一个bean
package com.javacode2018.lesson003.demo3.test2;
import org.springframework.stereotype.Component;
@Component
public class LessonModel {
//课程名称
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "LessonModel{" +
"name='" + name + '\'' +
'}';
}
}
上面这个bean有个name字段,并没有设置值,下面我们在BeanFactoryPostProcessor来对其设置值。
自定义的BeanFactoryPostProcessor
下面代码中,我们先获取lessonModel
这个bean的定义信息,然后给其name
属性设置了一个值。
package com.javacode2018.lesson003.demo3.test2;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("准备修改lessonModel bean定义信息!");
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("lessonModel");
beanDefinition.getPropertyValues().add("name", "spring高手系列!");
}
}
测试用例
@Test
public void test2() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig2.class);
context.refresh();
System.out.println(context.getBean(LessonModel.class));
}
运行输出
准备修改lessonModel bean定义信息!
LessonModel{name='spring高手系列!'}
结果中可以看出,通过BeanFactoryPostProcessor
修改了容器中已经注册的bean定义信息。
这个接口的几个重要实现类
PropertySourcesPlaceholderConfigurer
这个接口做什么的,大家知道么?来看一段代码
<bean class="xxxxx">
<property name="userName" value="${userName}"/>
<property name="address" value="${address}"/>
</bean>
这个大家比较熟悉吧,spring就是在PropertySourcesPlaceholderConfigurer#postProcessBeanFactory
中来处理xml中属性中的${xxx}
,会对这种格式的进行解析处理为真正的值。
CustomScopeConfigurer
向容器中注册自定义的Scope对象,即注册自定义的作用域实现类,关于自定义的作用域,不了解的朋友,建议先看一下:Spring系列第6篇:玩转bean scope,避免跳坑里!
这个用法比较简单,定义一个CustomScopeConfigurer
的bean就可以了,然后通过这个类来注册自定义的bean。
EventListenerMethodProcessor
处理@EventListener
注解的,即spring中事件机制,需要了解spring事件的:spring事件机制详解
还有一些实现类,这里就不介绍了。
使用注意
BeanFactoryPostProcessor
接口的使用有一个需要注意的地方,在其postProcessBeanFactory
方法中,强烈禁止去通过容器获取其他bean,此时会导致bean的提前初始化,会出现一些意想不到的问题,因为这个阶段中BeanPostProcessor
还未准备好,本文开头4个阶段中有介绍,BeanPostProcessor
是在第3个阶段中注册到spring容器的,而BeanPostProcessor
可以对bean的创建过程进行干预,比如spring中的aop就是在BeanPostProcessor
的一些子类中实现的,@Autowired
也是在BeanPostProcessor
的子类中处理的,此时如果去获取bean,此时bean不会被BeanPostProcessor
处理,所以创建的bean可能是有问题的,还是通过一个案例给大家演示一下把,通透一些。
来一个简单的类
package com.javacode2018.lesson003.demo3.test3;
import org.springframework.beans.factory.annotation.Autowired;
public class UserModel {
@Autowired
private String name; //@1
@Override
public String toString() {
return "UserModel{" +
"name='" + name + '\'' +
'}';
}
}
@1:使用了@Autowired,会指定注入
来个配置类
配置类中定义了2个UserModel类型的bean:user1、user2
并且定义了一个String类型的bean:name,这个会注入到UserModel中的name属性中去。
package com.javacode2018.lesson003.demo3.test3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class MainConfig3 {
@Bean
public UserModel user1() {
return new UserModel();
}
@Bean
public UserModel user2() {
return new UserModel();
}
@Bean
public String name() {
return "路人甲Java,带大家成功java高手!";
}
}
测试用例
输出容器中所有UserModel类型的bean
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig3.class);
context.refresh();
context.getBeansOfType(UserModel.class).forEach((beanName, bean) -> {
System.out.println(String.format("%s->%s", beanName, bean));
});
}
运行输出
user1->UserModel{name='路人甲Java,带大家成功java高手!'}
user2->UserModel{name='路人甲Java,带大家成功java高手!'}
效果不用多解释,大家一看就懂,下面来重点。
添加一个BeanFactoryPostProcessor
在postProcessBeanFactory
方法中去获取一下user1这个bean
package com.javacode2018.lesson003.demo3.test3;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.getBean("user1");
}
}
再次运行输出
user1->UserModel{name='null'}
user2->UserModel{name='路人甲Java,带大家成功java高手!'}
注意,user1中的name变成null了,什么情况?
是因为@Autowired注解是在AutowiredAnnotationBeanPostProcessor
中解析的,spring容器调用BeanFactoryPostProcessor#postProcessBeanFactory
的使用,此时spring容器中还没有AutowiredAnnotationBeanPostProcessor
,所以此时去获取user1这个bean的时候,@Autowired并不会被处理,所以name是null。
源码
4个阶段的源码
4个阶段的源码为位于下面这个方法中
org.springframework.context.support.AbstractApplicationContext#refresh
这个方法中截取部分代码如下:
// 对应阶段1和阶段2:调用上下文中注册为bean的工厂处理器,即调用本文介绍的2个接口中的方法
invokeBeanFactoryPostProcessors(beanFactory);
// 对应阶段3:注册拦截bean创建的bean处理器,即注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 对应阶段3:实例化所有剩余的(非延迟初始化)单例。
finishBeanFactoryInitialization(beanFactory);
阶段1和阶段2的源码位于下面方法中,代码比较简单,强烈建议大家去看一下,几分钟就可以看懂了。
org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)
总结
-
注意spring的4个阶段:bean定义阶段、BeanFactory后置处理阶段、BeanPostProcessor注册阶段、单例bean创建组装阶段
-
BeanDefinitionRegistryPostProcessor会在第一个阶段被调用,用来实现bean的注册操作,这个阶段会完成所有bean的注册
-
BeanFactoryPostProcessor会在第2个阶段被调用,到这个阶段时候,bean此时已经完成了所有bean的注册操作,这个阶段中你可以对BeanFactory中的一些信息进行修改,比如修改阶段1中一些bean的定义信息,修改BeanFactory的一些配置等等
-
阶段2的时候,2个禁止操作:禁止注册bean、禁止从容器中获取bean
-
本文介绍的2个接口的实现类可以通过
PriorityOrdered
接口或者Ordered
接口来指定顺序
案例源码
https://gitee.com/javacode2018/spring-series
路人甲java所有案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。
课后作业
2个问题,欢迎留言讨论,会有红包惊喜哦!
问题1
大家去读一下org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors的源码,将内部主要的执行过程通过文字描述出来,然后留言
问题2
BeanPostProcessor和BeanFactoryPostProcessors有何区别?留言
来源:https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648934655&idx=1&sn=5b6c360de7eda0ca83d38e9db3616761&chksm=886210c1bf1599d7c42919a8b883a7cd2dd8e42212627a32e6d91dfb1f6da1b9536079ec4f6d&token=1804011114&lang=zh_CN&scene=21#wechat_redirect