品Spring:详细解说bean后处理器
一个小小的里程碑
首先感谢能看到本文的朋友,感谢你的一路陪伴。
如果每篇都认真看的话,会发现本系列以bean定义作为切入点,先是详细解说了什么是bean定义,接着又强调了bean定义为什么如此重要。
然后又讲了获取bean定义详细信息的方法,接着又讲了bean定义注册的若干种方式,然后是bean定义注册方式的实现细节。
最后又以SpringBoot应用为例,从容器启动前、启动后分两个阶段解说bean定义是如何进入到容器里的。
就是bean工厂后处理器配合使用@ComponentScan注解和@Import注解,一起完了所有bean定义的注册。
当bean定义注册完毕后,紧接着就是对单例(singleton)bean的实例化。此时容器处在启动中。
之前面试别人时问过一个问题,为什么Spring要在容器启动时就实例化所有单例bean,而不是放到首次使用时?
记忆中没有人回答到点上,原因很简单,就是为了提前发现潜在的错误。启动时报错比运行时报错好得多。
OK,从现在开始将进入一个新的阶段,即bean的实例化,bean的依赖装配,bean的初始化。
为了能够深度参与这个过程,使之更加灵活可配,Spring引入了bean后处理器的概念。
五个bean后处理器接口
bean后处理器主要应用于bean的创建过程中的一些操作,如检测下是否实现了指定接口,或用一个代理包装下bean实例,把代理返回等等。
首先来看第一个接口,BeanPostProcessor,如下图01:

可以看到这是一组对称的方法,一个是BeforeInitialization,一个是AfterInitialization。
一个在初始化前,一个在初始化后。所以首先要搞清楚什么是初始化?
在Spring中,初始化指的是在bean实例上执行一个特定的方法,该方法就称为初始化方法。
编程新说注:一般情况下,一个类一个初始化方法就够了,也可以有多个,Spring的源码实现是支持的。
那么如何指定这个初始化方法呢?共有三种方案可选:
1)实现Spring提供的一个接口,InitializingBean,它只有一个方法,就是afterPropertiesSet。
2)使用@Bean注解注册bean定义时,设置注解的initMethod属性为bean的一个方法名。
3)使用java的注解@PostConstruct,把它标在bean的一个方法上。
这三种方式指定的方法都是初始化方法,所谓初始化就是调用这些方法。
所以这个接口的两个方法的调用位置就是:
bean的实例化-> bean的依赖装配 -> 接口方法一(初始化前) -> bean的初始化方法 -> 接口方法二(初始化后) -> OK。
我们可以看到,这个接口的切入位置是在bean的依赖已经装配好之后,似乎有些“晚了”,因为这样只能参与bean的初始化,有没有稍靠前的?
当然有了,接着看第二个接口,InstantiationAwareBeanPostProcessor,如下图02:

接口的名字中有InstantiationAware,说明是“实例化感知”的bean后处理器。即可以参与到bean的实例化过程中。确实比上一个接口提前了。
接口有三个方法,一个是BeforeInstantiation,一个是AfterInstantiation,一个是Properties。光从名字上就能看出个七七八八了。
所以这个接口的三个方法的调用位置就是:
bean的实例化准备阶段 -> 接口方法一(实例化前)-> bean的实例化 -> 接口方法二(实例化后) -> 接口方法三(定制bean所需的属性值) -> bean的属性设置 -> OK。
第一个方法在bean实例化前调用,如果返回一个非null对象,则Spring就使用这个对象了,不再进行实例化了。
所以这里可以返回一个目标bean的代理,来压制(延迟)目标bean的实例化。
这个方法的参数是bean的类型,因为此时还没有bean实例呢。
第二个方法在bean实例化后且属性设置(显式的属性设置或依赖的装配)前调用。
这是一个理想的地方用来执行自定义字段注入,因为此时Spring的自动装配尚未到来。
通常方法返回true,如果返回false,后续的属性设置将被跳过。
同时,后面的该接口类型的实例都将不会再在这个bean实例上调用。
第三个方法在bean属性设置前调用,可以用来定制即将为bean实例设置的属性。
方法pvs是传进来的已有属性。方法默认返回null。表示不对属性进行操作。
第四个方法现已经废除,等它移除后,第三个方法将默认返回pvs。
下面看第三个接口,SmartInstantiationAwareBeanPostProcessor,也是和bean创建相关的,如下图03:

这个接口也有三个方法:
第一个方法,用来预测最终的bean类型,这是给我们提供一个修改bean类型的机会,方法的参数是原始的bean类型。
如果方法返回null,则不进行预测,按照Spring自己的逻辑走。
第二个方法,用来确定候选的构造方法,给我们一个定制构造方法的机会,方法的参数是原始的bean类型。
如果方法返回null,则不进行指定,按照Spring自己的逻辑去判断出最适合的构造方法。
第三个方法,用来获取一个早期bean实例的引用。为什么说是早期呢?因为bean实例的初始化方法还没有执行。
编程新说注:可以认为此时的bean还处于一种不完善的状态。
典型的用法是可以用来解决循环引用。这个地方可以在目标bean完全初始化之前较早地暴露一个包装器。
第四个接口,说完了bean的创建,再来看看bean的销毁,DestructionAwareBeanPostProcessor,如下图04:

这个接口比较简单,只有两个方法:
第一个方法,在bean实例销毁前会被调用,来执行一些定制的销毁代码。
这些销毁代码通常位于一个方法里,叫做销毁方法,是与初始化方法对应的。
同样也有三种方式来指定销毁方法:
1)实现Spring提供的一个接口,DisposableBean,它只有一个方法destroy。
2)使用@Bean注解注册bean定义时,设置注解的destroyMethod属性为bean的一个方法名。
3)使用java的注解@PreDestroy,把它标在bean的一个方法上。
这三种方式指定的都是销毁方法。如果指定了的话,就在刚刚的接口方法里调用了。
只有被容器完全管理生命周期的bean才会应用,如singleton和scoped的bean实例。
第二个方法,就是决定是否要为bean实例调用第一个方法来执行一些销毁代码。
返回true表示需要,false表示不需要调用。
因为第五个接口是和bean定义有关系的,所以先来看看bean定义的实现类都有哪些。
有几个类需要了解一下,如下图05:

1)AbstractBeanDefinition,是所有bean定义的父类。
2)RootBeanDefinition,是在XML配置时代,注册bean定义时用的类。
3)ChildBeanDefinition,是在XML配置时代,注册bean定义时用的类,必须在配置时指定一个父bean定义。
4)GenericBeanDefinition,在注解配置时代,推荐使用的bean定义类,可以在运行时动态指定一个父bean定义,也可以不指定。
5)AnnotatedGenericBeanDefinition,在注解配置时代,通过编程方式注册bean定义时用的类,继承了GenericBeanDefinition。
6)ScannedGenericBeanDefinition,在注解配置时代,通过扫描jar包中.class文件的方式注册bean定义时用的类,继承了GenericBeanDefinition。
来分析一下,第3必须有一个父bean定义,第4可以有一个父bean定义,第5、6继承自第4。第1是一个抽象类。
所以只有第2是一个没有父bean定义且非抽象的类,因此,Spring会先把bean定义转换为第2。然后再生成bean实例。
因为可能存在父子关系,所以需要合并bean定义。父子关系其实就是一种“继承”和“重写”。
子可以继承父的信息,也可以重写父的信息,同样也有些信息不继承,只使用子自己的。
这种继承可以是多级的,如A继承B,B继承C,C继承D。
在合并bean定义时,会把A、B、C、D合起来变成一个M,但是ABCD本身不会再被改变。
合成的bean定义M会被缓存起来,就是用它来生成bean实例的。
这些基本知识了解了之后,接下来看最后一个接口。
第五个接口,MergedBeanDefinitionPostProcessor,如下图06:

这个接口的主要目的不是用来修改合并后的bean定义的,虽然也可以进行一些修改。
它主要用来进行一些自省操作,如一些检测,或在处理bean实例之前缓存一些相关的元数据。
这些作用都在第一个方法里实现。第二个方法是一个通知方法,当一个bean定义被重置时调用。
这个方法用于清除和受影响的bean相关的任何元数据。
>>> 品Spring系列文章 <<<
品Spring:SpringBoot和Spring到底有没有本质的不同?
品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”
品Spring:SpringBoot发起bean定义注册的“二次攻坚战”
品Spring:注解之王@Configuration和它的一众“小弟们”
>>> 热门文章集锦 <<<
爸爸又给Spring MVC生了个弟弟叫Spring WebFlux
【面试】吃透了这些Redis知识点,面试官一定觉得你很NB(干货 | 建议珍藏)
【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看(建议珍藏)
【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生(深度好文,建议珍藏)
作者是工作超过10年的码农,现在任架构师。喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。下面是公众号和知识星球的二维码,欢迎关注!

品Spring:详细解说bean后处理器的更多相关文章
- 半夜思考之查漏补缺, Spring 的 Bean 后处理器
有一篇写的是容器后处理器, 这篇是 Bean 后处理器 , 我对这个 Bean 后处理器的理解就是一个 AOP 编程 . Bean 后处理器 : 是一种特殊的 Bean , 这种 Bean 不对外提供 ...
- Spring之bean后处理器
Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化时,均会自动执行该类的两个方法.由于该Bean是由其它Bean自动调用执行,不是程序员手工调用,故此Bean无须id属性.需要做的是, ...
- 品Spring:bean工厂后处理器的调用规则
上一篇文章介绍了对@Configuration类的处理逻辑,这些逻辑都写在ConfigurationClassPostProcessor类中. 这个类不仅是一个“bean工厂后处理器”,还是一个“be ...
- Spring Bean后处理器以及容器后处理器【转】
Bean后处理器:即当spring容器实例化Bean实例之后进行的增强处理. 容器后处理器:对容器本身进行处理,并总是在容器实例化其他任何Bean之前读取配置文件的元数据并可能修改这些数据. 一.Be ...
- 8 -- 深入使用Spring -- 1...2 Bean后处理器的用处
8.1.2 Bean后处理器的用处 Spring提供的两个常用的后处理器: ⊙ BeanNameAutoProxyCreator : 根据Bean实例的name属性,创建Bean实例的代理. ⊙ De ...
- spring中的bean后处理器
package com.process; import org.springframework.beans.BeansException; import org.springframework.bea ...
- 8 -- 深入使用Spring -- 1...1Bean后处理器
8.1.1 Bean后处理器(BeanPostProcessor) Bean后处理器主要负责对容器中其他Bean执行后处理,例如为容器中的目标Bean生成代理等. Bean后处理器会在Bean实例创建 ...
- 品Spring:bean定义上梁山
认真阅读,收获满满,向智慧又迈进一步... 技术不枯燥,先来点闲聊 先说点好事高兴一下.前段时间看新闻说,我国正式的空间站建设已在进行当中.下半年,长征五号B运载火箭将在海南文昌航天发射场择机将空间站 ...
- spring中Bean后置处理器实现总结
BeanPostProcessor接口 bean的后置处理器实现功能主要是 可以在bean初始化之前和之后做增强处理.自定义MyBeanProcessor实现BeanPostProcessor接口,重 ...
随机推荐
- 使用VS Code 开发.NET CORE 程序指南
1. 前言 近两年来,很多前端的同学都开始将 VSCode 作为前端主力开发工具,其丰富的扩展给程序开发尤其是前端开发带来了很多便利,但是作为微软主力语言的 .NET,却由于有宇宙第一编辑器 Visu ...
- java.lang.Thread类详解
java.lang.Thread类详解 一.前言 位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前 ...
- 重识 ArrayList
前言 ArrayList 作为 Java 集合框架中最常用的类,在一般情况下,用它存储集合数据最适合不过.知其然知其所以然,为了能更好地认识和使用 ArrayList,本文将从下面几方面深入理解 Ar ...
- DB2 根据id查表
SELECT * FROM SYSCAT.TABLES WHERE TBSPACEID = 2 AND TABLEID = 50 SELECT * FROM SYSCAT.COLUMNS WHERE ...
- P1357 花园 状压 矩阵快速幂
题意 小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(2<=N<=10^15).他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻M(2<=M& ...
- 左偏树 P3377【模板】左偏树(可并堆)
题目传送门 代码: /* code by: zstu wxk time: 2019/03/01 */ #include<bits/stdc++.h> using namespace std ...
- 最小生成树问题---Prim算法学习
一个具有n个节点的连通图的生成树是原图的最小连通子集,它包含了n个节点和n-1条边.若砍去任一条边,则生成树变为非连通图:若增加一条边,则在图中形成一条回路.本文所写的是一个带权的无向连通图中寻求各边 ...
- DAX 第八篇:表连接
表连接是指两张表根据关联字段,组合成一个数据集.表连接不仅可以利用数据模型中已有的关系,而且可以利用DAX表达式基于表的任意列定义连接条件.因此,在DAX中,实现表与表之间的连接,有两种方式: 第一种 ...
- IDEA中创建maven web项目
本文将带你一路从IDEA中maven的配置到创建maven web项目,掌握IDEA中maven的使用. 一.IDEA中配置maven 开发中一般我们使用自己下载的maven,不使用IDEA工具自带的 ...
- Vue中如何使用less
最近发现好多小伙伴在面试的过程中会问到vue如何使用less和scss,所以我绝对更新.复习一下less:废话不多说直接进主题: 依赖下载 1.首先使用npm下载依赖: npm install --s ...