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;@Componentpublic class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {//定义一个字符串类型的beanAbstractBeanDefinition userNameBdf = BeanDefinitionBuilder.genericBeanDefinition(String.class).addConstructorArgValue("路人").getBeanDefinition();//将userNameBdf注册到spring容器中registry.registerBeanDefinition("userName", userNameBdf);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}}
同包中来个配置类
package com.javacode2018.lesson003.demo3.test0;import org.springframework.context.annotation.ComponentScan;@ComponentScanpublic 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 {@Testpublic 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;@Componentpublic class BeanDefinitionRegistryPostProcessor1 implements BeanDefinitionRegistryPostProcessor, Ordered {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {System.out.println(String.format("BeanDefinitionRegistryPostProcessor1{order=%d},注册name bean,", this.getOrder()));//定义一个beanAbstractBeanDefinition nameBdf = BeanDefinitionBuilder.genericBeanDefinition(String.class).addConstructorArgValue("路人甲java").getBeanDefinition();//将定义的bean注册到容器registry.registerBeanDefinition("name", nameBdf);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Overridepublic int getOrder() {return 2;}}
第二个
package com.javacode2018.lesson003.demo3.test1;@Componentpublic class BeanDefinitionRegistryPostProcessor2 implements BeanDefinitionRegistryPostProcessor, Ordered {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {System.out.println(String.format("BeanDefinitionRegistryPostProcessor2{order=%d},注册car bean,", this.getOrder()));//定义一个beanAbstractBeanDefinition nameBdf = BeanDefinitionBuilder.genericBeanDefinition(String.class).addConstructorArgValue("保时捷").getBeanDefinition();//将定义的bean注册到容器registry.registerBeanDefinition("car", nameBdf);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Overridepublic int getOrder() {return 1;}}
上面2个类中的postProcessBeanDefinitionRegistry方法第一行都有输出,一个可以通过运行结果看到执行的顺序。
同包中添加配置类
package com.javacode2018.lesson003.demo3.test1;import org.springframework.context.annotation.ComponentScan;@ComponentScanpublic class MainConfig1 {}
测试案例
@Testpublic 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,来看一下这个接口的源码:
@FunctionalInterfacepublic 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;@Componentpublic class LessonModel {//课程名称private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic 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;@Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("准备修改lessonModel bean定义信息!");BeanDefinition beanDefinition = beanFactory.getBeanDefinition("lessonModel");beanDefinition.getPropertyValues().add("name", "spring高手系列!");}}
测试用例
@Testpublic 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对象,即注册自定义的作用域实现类,关于自定义的作用域,不了解的朋友,建议先看一下:
这个用法比较简单,定义一个CustomScopeConfigurer的bean就可以了,然后通过这个类来注册自定义的bean。
EventListenerMethodProcessor
处理@EventListener注解的,即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 {@Autowiredprivate String name; //@1@Overridepublic 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@ComponentScanpublic class MainConfig3 {@Beanpublic UserModel user1() {return new UserModel();}@Beanpublic UserModel user2() {return new UserModel();}@Beanpublic String name() {return "路人甲Java,带大家成功java高手!";}}
测试用例
输出容器中所有UserModel类型的bean
@Testpublic 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;@Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic 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处理器,即注册BeanPostProcessorregisterBeanPostProcessors(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有何区别?留言
最新资料
注意:本文归作者所有,未经作者允许,不得转载