BeanPostProcessor和BeanFactoryPostProcessor族类原理及使用

1. 作用及区别

Spring+IOC容器源码分析 一文中的分析中,贯穿整个过程主线的大概是这样一条路线:

  • 加载Bean定义信息
  • 处理加载的Bean定义信息
  • 从Bean定义创建Bean实例
  • Bean创建的后置处理(属性填充、初始化回调等等)

当然这其中还有相当多的实现细节,包括 容器加载前的准备、Bean容器的准备等等。

这篇文章要说的是Spring框架中两个相当重要的类族:BeanFactoryPostProcessorBeanPostProcessor

其应用的位置对应于上面的过程2和4,也就是:

  • BeanFactoryPostProcessor:在实例化之前,整合 BeanDefinition 的过程中调用;操作对象为Bean定义
  • BeanPostProcessor:在BeanDefinition注册完成之后进行注册,在创建Bean过程中的实例化前后分别调用其中定义的方法;其操作对象为:已经实例化且进行了属性填充待初始化的Bean实例

注意要区分这两个类的作用以及调用时间和作用对象都是不同的

2. 常见的实现类解析

下面这些实现类都是在分析SpringIOC源码时提到过的,在这里我们再详细的看一下他们的作用以及调用事件

本文中提到的所有这些实现类都是在SpringIOC源码分析中提到过的,其在源码中具体的使用位置可以查看 SpringIOC源码分析 等三篇文章

2.1 BeanFactoryPostProcessor 的常见实现类

2.1.2 BeanDefinitionRegistryPostProcessor:

  1. 官方注解:对标准 BeanFactoryPostProcessor SPI的扩展,允许在常规BeanFactoryPostProcessor检测启动之前注册进一步的bean定义。特别地,BeanDefinitionRegistryPostProcessor可以注册更多的bean定义,这些定义反过来定义BeanFactoryPostProcessor实例

也就是说首先作为一个 BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor 也可以对应用程序上下文的内部Bean工厂进行其初始化后的修改,也就是可以允许修改Bean定义,例如重写或者添加Bean属性
另外的,作为 BeanDefinitionRegistryPostProcessor 自身这样一个特殊的 BeanFactoryPostProcessor来讲,其接口中的方法允许自定义的去添加更多的Bean定义,例如在这里去添加更多的 BeanFactoryPostProcessor 实例

总结一下:BeanFactoryPostProcessor可以修改各个注册的Bean,BeanDefinitionRegistryPostProcessor则可以动态的注册Bean到容器中

  1. 使用:BeanDefinitionRegistryPostProcessor 在Spring源码中的使用主要是在这块:invokeBeanFactoryPostProcessors(),具体的调用过程分析可以查看 Spring+IOC容器源码分析 文中关于 invokeBeanFactoryPostProcessors的总结

2.1.1 ConfigurationClassPostProcessor

  1. ConfigurationClassPostProcessor 是 BeanDefinitionRegistryPostProcessor 的实现类,是用于对Configuration类的引导处理的BeanFactoryPostProcessor的扩展

在当下基于springBoot注解开发应用已经相当广泛的情况下,可以说这个后置处理器是整个 spring中最最核心的处理器也不为过。
不同于xml方式通过解析xml文件获取到所有Bean定义并将其注册到相应的缓存,基于注解配置后将所有Bean配置解析成BeanDefinition都是由这个处理器来完成的。例如我们常见的:@controller,@service,@mapper,@component,@configuration以及@Import等等

2.调用时机以及作用:

首先 ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessorPriorityOrdered接口,那么从我们在之前的文章对BeanFactoryPostProcess调用的方法 invokeBeanFactoryPostProcessors()的分析中:
ConfigurationClassPostProcessor 也是在这里进行调用的.通过其实现类,主要是调用其实现的两个方法:

  • postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry):

    解析了加了Configuration注解的类,同时解析出 @ComponentScan 和 @ComponentScans 扫描出的Bean,也会解析出加了 @Bean 注解的方法所注册的Bean,以及通过 @Import 注解注册的Bean和 @ImportResource 注解导入的配置文件中配置的Bean。最终将其加载到BeanDefinition的配置中

  • postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory):

    利用CGLIB对加了@Configuration注解的类创建动态代理,进行增强。最后还会向spring容器中添加一个Bean后置处理器:ImportAwareBeanPostProcessor:用来设置@Import注解元数据

2.2 BeanPostProcessor的常见实现类

2.2.1 ApplicationContextAwareProcessor

顾名思义,ApplicationContextAwareProcessorBeanPostProcessor对应用上下文中的aware类的处理器扩展

首先了解下Spring中的aware类,aware意为"可感知的",在spring中主要是用来提供对某种容器级的Bean的便捷的获取和连接方式
例如:一般在我们的项目里边会有一个用来直接获取Bean的工具类,那么在这里就必须要拿到当前存放Bean的BeanFactory,有很多方式获取到.

  1. 实现BeanFactoryAware接口,调用setBeanFactory(BeanFactory beanFactory)将beanFactory赋值给本地beanFactory即可
  2. 实现ApplicationContextAware接口,调用setApplicationContext(ApplicationContext applicationContext)从applicationContext中获取到beanFactory赋值给本地beanFactory
  3. 实现BeanFactoryPostProcessor,拿到BeanFactory.(这里用BeanFactoryPostProcessor有点杀鸡用牛刀的感觉,本来是用来做BeanDefinition修改的,这里只是使用了其容器的引用)
  4. 直接注入BeanFactory,再使用自定义的初始化将其赋值到本地

从IOC源码分析中,我们知道其实最终创建的BeanFactory是一个DefaultListableBeanFactory,因此在1、2中的BeanFactory可以直接强转为DefaultListableBeanFactory,以此可以调用更多详细的获取Bean的方法

扯远了,想一个问题,无论是我们从aware接口获取值,还是直接注入值,这些aware接口是什么时候拿到这些引用的呢?这也就是这个processor的作用

Spring+IOC容器源码分析 一文中准备Bean容器的部分:
prepareBeanFactory(factory)中看到了在这里创建了一个ApplicationContextAwareProcessor并传入了当前上下文引用作为其构造参数
查看源码我们发现:

  • 它只实现了 BeanPostProcessor 的 postProcessBeforeInitialization()方法,并在其中对所有的aware类进行了值的设置,因此我们才能在其它类中通过这些aware拿到需要的实例引用;可以看到此处传入的context类型为ConfigurableApplicationContext
  • 调用时机:同BeanPostProcessor的调用时机: 属性填充后,真正实例化之前实例化

2.2.2 InstantiationAwareBeanPostProcessor 实例化可感知的BeanPostProcessor

该类相比较与父接口自定义了三个方法,这三个方法都是与bean的实例化相关的,这也印证了该类型名称:InstantiationAware

  1. 主要作用:对目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置
  2. InstantiationAwareBeanPostProcessor 接口方法的执行顺序:
  • InstantiationAwareBeanPostProcessor接口中的postProcessBeforeInstantiation,在实例化之前调用

    此时对象尚未实例化,因此可以使用这个方法的返回值来代替原本该生成的目标对象的实例(比如代理对象)

    如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走

  • Bean的实例化,调用构造方法

  • InstantiationAwareBeanPostProcessor接口中的postProcessAfterInstantiation,在实例化之后调用

    此时对象已被实例化,但是该实例的属性还未被设置,都是null
    该方法的返回值是决定要不要调用postProcessPropertyValues方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());如果该方法返回false,并且不需要check,那么postProcessPropertyValues就会被忽略不执行;如果返回true, postProcessPropertyValues就会被执行

  • InstantiationAwareBeanPostProcessor接口中的postProcessPropertyValues[当postProcessAfterInstantiation返回true才执行]

    对属性值进行修改(这个时候属性值还未被设置)

  • BeanPostProcessor接口中的postProcessBeforeInitialization,在初始化之前调用

  • InitializingBean中的afterProperties方法,执行初始化

  • BeanPostProcessor接口中的postProcessAfterInitialization,在实例化之后调用

    1. 从SpringIOC源码分析看该类的使用:
1. createBean--resolveBeforeInstantiation--applyBeanPostProcessorsBeforeInstantiation
2. populateBean
  • 在位置1处通过分析源码我们知道在这里调用了 postProcessBeforeInstantiation方法,用来在这里提前返回生成的对象实例,并直接执行 postProcessAfterInitialization方法
  • 在位置2处查看源码又有两处涉及到该类: 即对于上述中的其他两个方法的调用,执行postProcessAfterInstantiation并根据其返回值条件判断是否需要调用 postProcessPropertyValues 对对象进行属性修改

2.2.3 SmartInstantiationAwareBeanPostProcessor 循环引用获取早期引用

  1. InstantiationAwareBeanPostProcessor接口的扩展,添加了一个用于预测已处理bean的最终类型的回调.

  2. 方法分析:

    • Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException;

    预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null

    • Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException;

    选择合适的构造器,比如目标对象有多个构造器,在这里可以进行一些定制化,选择合适的构造器.beanClass参数表示目标实例的类型,beanName是目标实例在Spring容器中的name
    返回值是个构造器数组,如果返回null,会执行下一个PostProcessor的determineCandidateConstructors方法;否则选取该PostProcessor选择的构造器
    具体的使用:如AutowiredAnnotationBeanPostProcessor实现将自动扫描通过@Autowired/@Value注解的构造器从而可以完成构造器注入

    • Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;

    获得提前暴露的bean引用。主要用于解决循环引用的问题;只有单例对象才会调用此方法

2.2.4 MergedBeanDefinitionPostProcessor:合并Bean定义的PostProcessor

  1. 运行时对合并的bean定义的后处理器回调接口。BeanPostProcessor的子接口实现,以便对Spring BeanFactory用于创建bean实例的合并bean定义(原始bean定义的已处理副本)进行后处理。

例如,{@link#postprocessemergedbeandefinition}方法可以内省bean定义,以便在对bean的实际实例进行后处理之前准备一些缓存的元数据。它还允许修改bean定义,但仅限于实际用于并发修改的定义属性。本质上,这只适用于在{@link RootBeanDefinition}本身上定义的操作,而不适用于其基类的属性

  • void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

    后置处理对指定Bean的给定的合并Bean定义

  • default void resetBeanDefinition(String beanName) {}

    通知指定名称的bean定义已重置,此后处理器应清除受影响bean的所有元数据

  1. 使用的位置:doCreateBean()

2.2.5 AutowiredAnnotationBeanPostProcessor

BeanPostProcessor的实现,自动连接带注释的字段、setter方法和任意配置方法。要注入的这些成员是通过注释检测到的:默认情况下,处理Spring的@Autowired和@Value注释
简单来说就是与Spring的autoWired自动注入相关的一个BeanPostProcessor,我们之所以能方便的使用@Autowired注入其他Bean都要归功于这个注解

关于该类详细的解析将在自动注入原理与AutowiredAnnotationBeanPostProcessor 一文中

2.2.7 DestructionAwareBeanPostProcessor

BeanPostProcessor的子接口,用于添加销毁前回调。典型用法是对特定bean类型调用定制的销毁回调,匹配相应的初始化回调

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341

推荐阅读更多精彩内容