Spring官网阅读(六)容器的扩展点(一)BeanFactoryPostProcessor

以前的文章咱们已经学习完了BeanDefinition的基本概念跟合并,其中屡次提到了容器的扩展点,这篇文章咱们就开始学习这方面的知识。这部份内容主要涉及官网中的1.8小结。按照官网介绍来讲,容器的扩展点能够分类三类,BeanPostProcessor,BeanFactoryPostProcessor以及FactoryBean。本文咱们主要学习BeanFactoryPostProcessor,对应官网中内容为1.8.2小节java

01程序员

总览web

先看看官网是怎么说的:spring

从上面这段话,咱们能够总结以下几点:sql

  1. BeanFactoryPostProcessor能够对Bean配置元数据进行操做。也就是说,Spring容器容许 BeanFactoryPostProcessor读取指定Bean的配置元数据,并能够在Bean被实例化以前修改它。这里说的配置元数据其实就是咱们以前讲过的 BeanDefinition
  2. 咱们能够配置多个 BeanFactoryPostProcessor,而且只要咱们配置的 BeanFactoryPostProcessor同时实现了 Ordered接口的话,咱们还能够控制这些 BeanFactoryPostProcessor执行的顺序

接下来,咱们经过Demo来感觉下BeanFactoryPostProcessor的做用:apache

02微信

示例app

这里就以官网上的demo为例:编辑器

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">

    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

在上面的例子中,咱们配置了一个PropertyPlaceholderConfigurer,为了方便理解,咱们先分析下这个类,其UML类图以下:ide

在这里插入图片描述
  • Ordered用于决定执行顺序
  • PriorityOrdered,这个接口直接继承了 Ordered接口,而且没有作任何扩展。只是做为一个标记接口,也用于决定 BeanFactoryPostProcessor的执行顺序。在后文源码分析时,咱们会看到他的做用
  • Aware相关的接口咱们在介绍Bean的生命周期回调时统一再分析,这里暂且无论
  • FunctionalInterface,这是 java8新增的一个接口,也只是起一个标记做用,标记该接口是一个函数式接口。
  • BeanFactoryPostProcessor,表明这个类是一个Bean工厂的后置处理器。
  • PropertiesLoaderSupport,这个类主要包含定义了属性的加载方法,包含的属性以下:
// 本地属性,能够直接在XML中配置
@Nullable
protected Properties[] localProperties;

// 是否用本地的属性覆盖提供的文件中的属性,默认不会
protected boolean localOverride = false;

// 根据地址找到的对应文件
@Nullable
private Resource[] locations;

// 没有找到对应文件是否抛出异常,false表明不抛出
private boolean ignoreResourceNotFound = false;

// 对应文件资源的编码
@Nullable
private String fileEncoding;

// 文件解析器
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
  • PropertyResourceConfigurer,这个类主要能够对读取到的属性进行一些转换
  • PlaceholderConfigurerSupport,主要负责对占位符进行解析。其中几个属性以下:
// 默认解析的前缀
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
// 默认解析的后缀
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
// 属性名称跟属性值的分隔符
public static final String DEFAULT_VALUE_SEPARATOR = ":";
  • PropertyPlaceholderConfigurer继承了上面这些类的全部功能,同时能够配置属性的解析顺序
// 不在系统属性中查找
public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;

// 若是在配置文件中没有找到,再去系统属性中查找
public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;

// 先查找系统属性,没查到再去查找配置文件中的属性
public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;

对这个类有一些了解以后,咱们回到以前的例子中,为何在jdbc.properties文件中配置的属性值会被应用到

BasicDataSource这个Bean上呢?为了帮助你们理解,我画了一个图:

在这里插入图片描述

这个流程就如上图,能够看到咱们经过PropertyPlaceholderConfigurer这个特殊的BeanFactoryPostProcessor完成了BeanDefinition中的属性值中的占位符的替换。在BeanDefinition被解析出来后,Bean实例化以前对其进行了更改了。

在上图中,建立Bean的过程咱们暂且无论,还有一个问题咱们须要弄清楚,Spring是如何扫描并解析成BeanDefinition的呢?这里就不得不提到咱们接下来须要分析的这个接口了:BeanDefinitionRegistryPostProcessor,就将其简称为bdrpp

03

bdrpp

咱们先来看一下这个接口的UML类图:

在这里插入图片描述

从上图中,咱们能够得出两个结论:

  1. BeanDefinitionRegistryPostProcessor直接继承了 BeanFactoryPostProcessor,因此它也是一个Bean工厂的后置处理器
  2. Spring只提供了一个内置的 BeanDefinitionRegistryPostProcessor的实现类,这个类就是 ConfigurationClassPostProcessor,实际上咱们上面说的扫描解析成 BeanDefinition的过程就是由这个类完成的

咱们来看下这个接口定义:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

public interface BeanFactoryPostProcessor {
 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

相比于正常的BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor多提供了一个方法,那么多提供的这个方法有什么用呢?这个方法会在何时执行呢?这里我先说结论:

这个方法的左右也是为了扩展,相比于BeanFactoryPostProcessorpostProcessBeanFactory方法,这个方法的执行时机会更加靠前,Spring自身利用这个特性完成了BeanDefinition的扫描解析。咱们在对Spring进行扩展时,也能够利用这个特性来完成扫描这种功能,好比最新版的Mybatis就是这么作的。关于Mybatis跟Spring整合的过程,我打算在写完Spring的扫描以及容器的扩展点这一系列文章后单独用一篇文章来进行分析。

接下来,咱们直接分析其源码,验证上面的结论。

04

源码解析

在分析源码前,咱们看看下面这个图,以便你们对Spring的执行流程有个大概的了解:

在这里插入图片描述

上图表示的是形如AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class)的执行流程。咱们此次分析的代码主要是其中的3-5-1流程。对于的代码以下(代码比较长,咱们拆分红两部分分析):

BeanDefinitionRegistryPostProcessor执行流程
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    Set<String> processedBeans = new HashSet<>();
    // 这个if基本上必定会成立,除非咱们手动new了一个beanFactory
    if (beanFactory instanceof BeanDefinitionRegistry) {
        
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
       
        // 存储了只实现了BeanFactoryPostProcessor接口的后置处理器
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        
        // 存储了实现了BeanDefinitionRegistryPostProcessor接口的后置处理器
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
  
        // 这个beanFactoryPostProcessors集合通常状况下都是空的,除非咱们手动调用容器的addBeanFactoryPostProcessor方法
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
               
                // 执行实现了BeanDefinitionRegistryPostProcessor接口的后置处理器的postProcessBeanDefinitionRegistry方法,注意这里执行的不是postProcessBeanFactory方法,咱们上面已经讲过了,实现了BeanDefinitionRegistryPostProcessor接口的后置处理器有两个方法,一个是从父接口中继承而来的postProcessBeanFactory方法,另外一个是这个接口特有的postProcessBeanDefinitionRegistry方法
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
               
                // 保存执行过了的BeanDefinitionRegistryPostProcessor,这里执行过的BeanDefinitionRegistryPostProcessor只是表明它的特有方法:postProcessBeanDefinitionRegistry方法执行过了,可是千万记得,它还有一个标准的postProcessBeanFactory,也就是从父接口中继承的方法还未执行
                registryProcessors.add(registryProcessor);
           
            } else {
               
                // 将只实现了BeanFactoryPostProcessor接口的后置处理器加入到集合中
                regularPostProcessors.add(postProcessor);
            }
        }
  // 保存当前须要执行的实现了BeanDefinitionRegistryPostProcessor接口的后置处理器
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
  // 从容器中获取到全部实现了BeanDefinitionRegistryPostProcessor接口的Bean的名字
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
        for (String ppName : postProcessorNames) {
            // 判断这个类是否还实现了PriorityOrdered接口
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                // 若是知足条件,会将其建立出来,同时添加到集合中
                // 正常状况下,只会有一个,就是Spring容器本身提供的ConfigurationClassPostProcessor,Spring经过这个类完成了扫描以及BeanDefinition的功能
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        // 根据实现的PriorityOrdered接口进行拍讯
        sortPostProcessors(currentRegistryProcessors, beanFactory);
  
        // 将当前将要执行的currentRegistryProcessors所有添加到registryProcessors这个集合中
        registryProcessors.addAll(currentRegistryProcessors);
        
        // 执行后置处理器的逻辑,这里只会执行BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        
        // 清空集合
        currentRegistryProcessors.clear();
  
        // 这里从新获取实现了BeanDefinitionRegistryPostProcesso接口的后置处理器的名字,思考一个问题:为何以前获取了一次不能直接用呢?还须要获取一次呢?这是由于,在咱们上面执行过了BeanDefinitionRegistryPostProcessor中,能够在某个类中,咱们扩展的时候又注册了一个实现了BeanDefinitionRegistryPostProcessor接口的后置处理器
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
        for (String ppName : postProcessorNames) {
            // 确保没有被处理过而且实现了Ordered接口
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                // 加入到当前须要被执行的集合中
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
       
        // 根据ordered接口进行排序
        sortPostProcessors(currentRegistryProcessors, beanFactory);
       
        // 将当前将要执行的currentRegistryProcessors所有添加到registryProcessors这个集合中
        registryProcessors.addAll(currentRegistryProcessors);
       
        // 执行后置处理器的逻辑,这里只会执行BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        
        // 清空集合
        currentRegistryProcessors.clear();
  
        // 接下来这段代码是为了确认全部实现了BeanDefinitionRegistryPostProcessor的后置处理器可以执行完,之全部要一个循环中执行,也是为了防止在执行过程当中注册了新的BeanDefinitionRegistryPostProcessor
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            // 获取普通的BeanDefinitionRegistryPostProcessor,不须要实现PriorityOrdered或者Ordered接口
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    // 只要发现有一个须要执行了的后置处理器,就须要再次循环,由于执行了这个后置处理可能会注册新的BeanDefinitionRegistryPostProcessor
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }
    ......
BeanFactoryPostProcessor执行流程:
 ......承接上半部分代码......

        // 这里开始执行单独实现了BeanFactoryPostProcessor接口的后置处理器
        // 1.先执行实现了BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor,在前面的逻辑中咱们只执行了BeanDefinitionRegistryPostProcessor特有的postProcessBeanDefinitionRegistry方法,它的postProcessBeanFactory方法尚未被执行,它会在这里被执行
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        // 2.执行直接实现了BeanFactoryPostProcessor接口的后置处理器
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    } else {
  // 正常状况下,进不来这个判断,不用考虑
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
  // 获取全部实现了BeanFactoryPostProcessor接口的后置处理器,这里会获取到已经执行过的后置处理器,因此后面的代码会区分已经执行过或者未执行过
  String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, truefalse);

  // 保存直接实现了BeanFactoryPostProcessor接口和PriorityOrdered接口的后置处理器
  List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();

  // 保存直接实现了BeanFactoryPostProcessor接口和Ordered接口的后置处理器
  List<String> orderedPostProcessorNames = new ArrayList<>();

  // 保存直接实现了BeanFactoryPostProcessor接口的后置处理器,不包括那些实现了排序接口的类
  List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  for (String ppName : postProcessorNames) {
   if (processedBeans.contains(ppName)) {
                // 已经处理过了,直接跳过
   } 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);
   }
  }

  // 先执行实现了BeanFactoryPostProcessor接口和PriorityOrdered接口的后置处理器
  sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

  // 再执行实现了BeanFactoryPostProcessor接口和Ordered接口的后置处理器
  List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
  for (String postProcessorName : orderedPostProcessorNames) {
   orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  sortPostProcessors(orderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

  // 最后执行BeanFactoryPostProcessor接口的后置处理器,不包括那些实现了排序接口的类
  List<`1> nonOrderedPostProcessors = new ArrayList<>();
  for (String postProcessorName : nonOrderedPostProcessorNames) {
   nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

  // 将合并的BeanDefinition清空,这是由于咱们在执行后置处理器时,可能已经修改过了BeanDefinition中的属性,因此须要清空,以便于从新合并
  beanFactory.clearMetadataCache();

经过源码分析,咱们能够将整个Bean工厂的后置处理器的执行流程总结以下:

首先,要明白一点,上图分为左右两个部分,表明的不是两个接口,而是两个方法

  • 一个是 BeanDefinitionRegistryPostProcessor特有的 postProcessBeanDefinitionRegistry方法
  • 另一个是 BeanFactoryPostProcessorpostProcessBeanFactory方法

这里咱们以方法为维度区分更好说明问题,postProcessBeanDefinitionRegistry方法的执行时机早于postProcessBeanFactory。而且他们按照上图从左到右的顺序进行执行。

另外在上面进行代码分析的时候不知道你们有没有发现一个问题,当在执行postProcessBeanDefinitionRegistry方法时,Spring采用了循环的方式,不断的查找是否有新增的BeanDefinitionRegistryPostProcessor,就是下面这段代码:

boolean reiterate = true;
   while (reiterate) {
    reiterate = false;
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
    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();
   }

可是在执行postProcessBeanFactory并无进行相似的查找。这是为何呢?

笔者本身认为主要是设计使然,Spring在设计时postProcessBeanFactory这个方法不是用于从新注册一个Bean的,而是修改。咱们能够看下这个方法上的这段java doc

 /**
  * Modify the application context's internal bean factory after its standard
  * initialization. All bean definitions will have been loaded, but no beans
  * will have been instantiated yet. This allows for overriding or adding
  * properties even to eager-initializing beans.
  * @param beanFactory the bean factory used by the application context
  * @throws org.springframework.beans.BeansException in case of errors
  */

 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

其中最重要的一段话:All bean definitions will have been loaded,全部的beanDefinition都已经被加载了。

咱们再对比下postProcessBeanDefinitionRegistry这个方法上的java doc

 /**
  * Modify the application context's internal bean definition registry after its
  * standard initialization. All regular bean definitions will have been loaded,
  * but no beans will have been instantiated yet. This allows for adding further
  * bean definitions before the next post-processing phase kicks in.
  * @param registry the bean definition registry used by the application context
  * @throws org.springframework.beans.BeansException in case of errors
  */

 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

你们注意这段话,This allows for adding further bean definitions before the next post-processing phase kicks in.容许咱们在下一个后置处理器执行前添加更多的BeanDefinition

从这里,我相信你们更加能理解为何postProcessBeanDefinitionRegistry这个方法的执行时机要早于postProcessBeanFactory了。

05

几个问题

一、可不能够在BeanFactoryPostProcessor去建立一个Bean,这样有什么问题?

从技术上来讲这样是能够的,可是正常状况下咱们不应这样作,这是由于可能会存在该执行的Bean工厂的后置处理器的逻辑没有被应用到这个Bean上。

二、BeanFactoryPostProcessor能够被配置为懒加载吗?

不能配置为懒加载,即便配置了也不会生效。咱们将Bean工厂后置处理器配置为懒加载这个行为就没有任何意义

06

总结

在这篇文章中,咱们最须要了解及掌握的就是BeanFactoryPostProcessor执行的顺序,总结以下:

  • 先执行直接实现了 BeanDefinitionRegistryPostProcessor接口的后置处理器,全部实现了 BeanDefinitionRegistryPostProcessor接口的类有两个方法,一个是特有的 postProcessBeanDefinitionRegistry方法,一个是继承子父接口的 postProcessBeanFactory方法。
    • postProcessBeanDefinitionRegistry方法早于 postProcessBeanFactory方法执行,对于 postProcessBeanDefinitionRegistry的执行顺序又遵循以下原子
    • 执行完全部的 postProcessBeanDefinitionRegistry方法后,再执行实现了 BeanDefinitionRegistryPostProcessor接口的类中的 postProcessBeanFactory方法
    1. 先执行实现了 PriorityOrdered接口的类中的 postProcessBeanDefinitionRegistry方法
    2. 再执行实现了 Ordered接口的类中的 postProcessBeanDefinitionRegistry的方法
    3. 最后执行没有实现上面两个接口的类中的 postProcessBeanDefinitionRegistry的方法
  • 再执行直接实现了 BeanFactoryPostProcessor接口的后置处理器
    1. 先执行实现了 PriorityOrdered接口的类中的 postProcessBeanFactory方法
    2. 再执行实现了 Ordered接口的类中的 postProcessBeanFactory的方法
    3. 最后执行没有实现上面两个接口的类中的 postProcessBeanFactory的方法
往期精选
Spring官网阅读(一)容器及实例化
Spring官网阅读(二)依赖注入及方法注入
Spring官网阅读(三)自动注入
Spring官网阅读(四)BeanDefinition 上
Spring官网阅读(五)BeanDefinition 下
您点的每一个赞,我都认真当成了喜欢

本文分享自微信公众号 - 程序员DMZ(programerDmz)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索