背景
该文章中的Spring版本为5.2.12.RELEASE,对于不同的版本可能在某些实现上略有不同。不过不会影响整体学习。
在之前的讲BeanPostProcessor中讲过它可以对Spring Bean实例做扩展,而今天要说的BeanFactoryPostProcessor与BeanPostProcessor类似,从它名字也可以知道它主要是对BeanFactory做扩展。我们可以通过BeanFactoryPostProcessor对Spring Bean的元信息(即BeanDefinition)进行读取和修改,它的作用点是在Spring Bean实例化之前。
接口定义
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* 在BeanFactory标准初始化之后,修改应用程序上下文的内部BeanFactory。所有bean定义都将被加载,但尚未实例化任何bean。这甚至可以覆盖或添加属性,甚至可以用于初始化bean
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
有一点在代码注释中强调过,BeanFactoryPostProcessor可以与Bean定义进行交互并进行修改,但不能与Bean实例进行交互。这样做可能会导致bean实例化过早,从而违反了容器并造成了意外的副作用。如果需要bean实例交互,请考虑改为实现BeanPostProcessor。
使用
代码如下:
public class App1 {
public static void main(String[] args) {
//创建ClassPathXmlApplicationContext
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-bean-factory-post-processor.xml");
//获取并打印出user Bean
User user = context.getBean(User.class);
System.out.println(user);
//获取user BeanDefinition
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
BeanDefinition userBeanDefinition = beanFactory.getBeanDefinition("user");
//打印出user BeanDefinition中的值
for (PropertyValue propertyValue : userBeanDefinition.getPropertyValues()) {
String name = propertyValue.getName();
if (propertyValue.getValue() instanceof TypedStringValue){
TypedStringValue temp = (TypedStringValue) propertyValue.getValue();
System.out.printf("App1-修改之后:name = %s, value = %s\r\n",name,temp.getValue());
}
}
}
}
class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");
for (PropertyValue propertyValue : beanDefinition.getPropertyValues()) {
String name = propertyValue.getName();
if (propertyValue.getValue() instanceof TypedStringValue){
TypedStringValue temp = (TypedStringValue) propertyValue.getValue();
System.out.printf("MyBeanFactoryPostProcessor-修改之前:name = %s, value = %s\r\n",name,temp.getValue());
Optional.ofNullable(temp.getValue()).ifPresent(value -> temp.setValue(value.toUpperCase()));
}
}
}
}
class User{
private String userName;
public User() {
System.out.println("User构造方法被调用");
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
'}';
}
}
spring-bean-factory-post-processor.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.buydeem.factprybeanpostprocessor.User">
<property name="userName" value="tom"/>
</bean>
<bean class="com.buydeem.factprybeanpostprocessor.MyBeanFactoryPostProcessor"/>
</beans>
上面的代码中定义了一个MyBeanFactoryPostProcessor,它的主要作用就是将User中的userName值转为大写。最后打印结果如下:
MyBeanFactoryPostProcessor-修改之前:name = userName, value = tom
User构造方法被调用
User{userName='TOM'}
App1-修改之后:name = userName, value = TOM
下面我们分析打印结果:
MyBeanFactoryPostProcessor-修改之前:name = userName, value = tom
该结果是在postProcessBeanFactory方法中打印出来的。在我们的实现中,我们获取到在XML配置中定义的BeanDefinition信息,然后将userName的属性值修改为大写。
User构造方法被调用
User{userName='TOM'}
App1-修改之后:name = userName, value = TOM
该结果说明postProcessBeanFactory方法确实是在Spring Bean实例化之前,而我们修改了BeanDefinition导致创建的User实例的值为修改后的值,且我们修改的是BeanDefinition,将tom改成TOM。
BeanDefinitionRegistryPostProcessor
定义
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* 在容器初始化完成之后,可以修改BeanDefinition列表。
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
从定义中可以看出,BeanDefinitionRegistryPostProcessor为BeanFactoryPostProcessor的子接口,该接口提供的postProcessBeanDefinitionRegistry可以在容器初始化完成之后修改BeanDefinition列表。简单的说就是我们可以通过该接口增加或者删除容器中BeanDefinition的定义。
如何使用
代码如下:
public class App2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean-factory-post-processor.xml");
//获取并打印User
User user = context.getBean(User.class);
System.out.println(user);
}
}
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(User.class)
.addPropertyValue("userName", "tom")
.getBeanDefinition();
registry.registerBeanDefinition("user",beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
class User{
private String userName;
public User() {
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
'}';
}
}
bean-factory-post-processor.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.buydeem.spring.beanfactorypostprocessor.MyBeanDefinitionRegistryPostProcessor"/>
</beans>
在配置文件中我们只定义了MyBeanDefinitionRegistryPostProcessor,并没有定义User实例。但是我们还是能从容器中获取到User实例。这个实例就是我们在MyBeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法中添加的,最后容器成功的帮我们把User实例创建出来。
如何注册BeanFactoryPostProcessor
在我们之前的示例中我们只是在配置文件中定义了BeanFactoryPostProcessor,它是如何并注册到ApplicationContext中的呢?在定义的文档中有说明,总体来说有两种方式:在配置文件中定义,然后会并自动注册到容器中来;另一种就是通过API添加到容器中,通过ConfigurableApplicationContext提供的方法,我们可以手动将BeanFactoryPostProcessor注册到容器中。
对于上面两种方式,我们都可以通过AbstractApplicationContext中的源码来了解。
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
this.beanFactoryPostProcessors.add(postProcessor);
}
这个是ConfigurableApplicationContext接口的实现,代码很简单,就是通过一个beanFactoryPostProcessors列表来保存BeanFactoryPostProcessor。另外一种就是在配置文件中定义,然后自动应用。
从AbstractApplicationContext的refresh开始看,然后调用invokeBeanFactoryPostProcessors方法。这个方法的内容很简单,主要就是调用PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()),下面我们分析该方法就能了解它是如何实现的了。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// 存储已经执行完成的BeanFactoryPostProcessor
Set<String> processedBeans = new HashSet<>();
//判断beanFactory是不是BeanDefinitionRegistry,只有是该类型才能执行BeanDefinitionRegistryPostProcessor
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
//存储BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
//存储BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
//迭代处理已经在AbstractApplicationContext中的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
//如果BeanDefinitionRegistryPostProcessor则要执行postProcessBeanDefinitionRegistry方法,并将其添加到registryProcessors列表中
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
//如果是BeanFactoryPostProcessor则直接添加到regularPostProcessors列表中
else {
regularPostProcessors.add(postProcessor);
}
}
//用来临时存储BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
//执行实现了PriorityOrdered的BeanDefinitionRegistryPostProcessor。这个里面的是在配置文件中配置的BeanDefinitionRegistryPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
//通过将其添加到临时的currentRegistryProcessors和已执行的processedBeans列表中
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
//对BeanDefinitionRegistryPostProcessor排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
//添加到registryProcessors中
registryProcessors.addAll(currentRegistryProcessors);
//执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
//清空临时列表
currentRegistryProcessors.clear();
//与上一步一致,只不过换做成实现了PriorityOrdered的BeanDefinitionRegistryPostProcessor
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
//循环处理剩下的BeanDefinitionRegistryPostProcessor知道没有再出现
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
//执行所有的BeanFactoryPostProcessor,因为BeanDefinitionRegistryPostProcessor也是BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
//因为beanFactory不是BeanDefinitionRegistry,所以执行所有的BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
//处理再配置文件中所定义的BeanFactoryPostProcessor,之前处理的是已经添加到容器中的BeanFactoryPostProcessor和定义在配置文件中的BeanDefinitionRegistryPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
//将BeanFactoryPostProcessor按照不同类型(这里指的是是否有序)分组存储
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
//首先对实现了PriorityOrdered的进行排序,然后执行
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
//然后对实现了Ordered的进行排序,然后执行
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
//最后执行剩下的BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
beanFactory.clearMetadataCache();
}
代码看起来比较复杂,其实细看内容并不是很复杂。它主要就是做两件事,执行BeanDefinitionRegistryPostProcessor和执行BeanFactoryPostProcessor。从源码也知道,BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法是会早于BeanFactoryPostProcessor中的postProcessBeanFactory方法;同时与BeanPostProcessor类似,他们都是可以多个的,同时还可以定义顺序。
Spring源码中的应用
BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor在Spring框架中也有很多应用。
PropertySourcesPlaceholderConfigurer
该类作为BeanFactoryPostProcessor的子类,它可以用来处理配置中的${...}占位符信息,它的使用示例如下:
public class App2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-bean-factory-post-processor-2.xml");
DbInfo dbInfo = context.getBean(DbInfo.class);
System.out.println(dbInfo);
}
}
@Data
@ToString
class DbInfo{
private String url;
private String userName;
}
spring-bean-factory-post-processor-2.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations" value="classpath*:jdbc.properties"/>
</bean>
<bean id="dbInfo" class="com.buydeem.factprybeanpostprocessor.DbInfo">
<property name="url" value="${jdbc.url}"/>
<property name="userName" value="${jdbc.userName}"/>
</bean>
</beans>
jdbc.properties配置文件如下:
jdbc.url=jdbcurl
jdbc.userName=tom
最后打印结果如下:
DbInfo(url=jdbcurl, userName=tom)
从结果我们可以知道,在xml配置文件中的${jdbc.url}
和${jdbc.userName}
最终被jdbc.properties中的内容替换,而它的实现则是通过BeanFactoryPostProcessor实现的。简单的说就是我们在postProcessBeanFactory方法中将BeanDefinition中的占位符替换成properties文件中的对应值即可。
ConfigurationClassPostProcessor
在Spring中我们经常使用@Configuration注解的、@Component、@ComponentScan、@Import、@ImportResource或者@Bean注解的用来标记配置类,而ConfigurationClassPostProcessor作为BeanDefinitionRegistryPostProcessor的子类,就是用来处理这些配置类,然后从其中解析出BeanDefinition信息注册到容器中。
总结
对于BeanFactoryPostProcessor来说,它针对BeanFactory做扩展,即Spring会在BeanFactory初始化之后,所有的BeanDefinition都已经加载,但是Bean实例还未创建前调用,我们可以对BeanDefinition做修改。
而BeanDefinitionRegistryPostProcessor的调用会在BeanFactoryPostProcessor之前,我们可以通过它在BeanFactoryPostProcessor之前注册更多的BeanDefinition。