0
点赞
收藏
分享

微信扫一扫

Spring之BeanFactoryPostProcessor

boom莎卡拉卡 2021-09-25 阅读 86
spring

背景

该文章中的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。

举报

相关推荐

0 条评论