品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:
 

640?wx_fmt=png


可以看到这是一组对称的方法,一个是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:
 

640?wx_fmt=png


接口的名字中有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:
 

640?wx_fmt=png


这个接口也有三个方法:

第一个方法,用来预测最终的bean类型,这是给我们提供一个修改bean类型的机会,方法的参数是原始的bean类型。

如果方法返回null,则不进行预测,按照Spring自己的逻辑走。

第二个方法,用来确定候选的构造方法,给我们一个定制构造方法的机会,方法的参数是原始的bean类型。

如果方法返回null,则不进行指定,按照Spring自己的逻辑去判断出最适合的构造方法。

第三个方法,用来获取一个早期bean实例的引用。为什么说是早期呢?因为bean实例的初始化方法还没有执行。

编程新说注:可以认为此时的bean还处于一种不完善的状态。

典型的用法是可以用来解决循环引用。这个地方可以在目标bean完全初始化之前较早地暴露一个包装器。


第四个接口,说完了bean的创建,再来看看bean的销毁,DestructionAwareBeanPostProcessor,如下图04:
 

640?wx_fmt=png


这个接口比较简单,只有两个方法:

第一个方法,在bean实例销毁前会被调用,来执行一些定制的销毁代码。

这些销毁代码通常位于一个方法里,叫做销毁方法,是与初始化方法对应的。

同样也有三种方式来指定销毁方法:

1)实现Spring提供的一个接口,DisposableBean,它只有一个方法destroy。

2)使用@Bean注解注册bean定义时,设置注解的destroyMethod属性为bean的一个方法名。

3)使用java的注解@PreDestroy,把它标在bean的一个方法上。

这三种方式指定的都是销毁方法。如果指定了的话,就在刚刚的接口方法里调用了。

只有被容器完全管理生命周期的bean才会应用,如singleton和scoped的bean实例。

第二个方法,就是决定是否要为bean实例调用第一个方法来执行一些销毁代码。

返回true表示需要,false表示不需要调用。


因为第五个接口是和bean定义有关系的,所以先来看看bean定义的实现类都有哪些。

有几个类需要了解一下,如下图05:
 

640?wx_fmt=png


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:
 

640?wx_fmt=png


这个接口的主要目的不是用来修改合并后的bean定义的,虽然也可以进行一些修改。

它主要用来进行一些自省操作,如一些检测,或在处理bean实例之前缓存一些相关的元数据。

这些作用都在第一个方法里实现。第二个方法是一个通知方法,当一个bean定义被重置时调用。

这个方法用于清除和受影响的bean相关的任何元数据。
 

>>> 品Spring系列文章 <<<

 

品Spring:帝国的基石

品Spring:bean定义上梁山

品Spring:实现bean定义时采用的“先进生产力”

品Spring:注解终于“成功上位”

品Spring:能工巧匠们对注解的“加持”

品Spring:SpringBoot和Spring到底有没有本质的不同?

品Spring:负责bean定义注册的两个“排头兵”

品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”

品Spring:SpringBoot发起bean定义注册的“二次攻坚战”

品Spring:注解之王@Configuration和它的一众“小弟们”

品Spring:bean工厂后处理器的调用规则

品Spring:详细解说bean后处理器

品Spring:对@PostConstruct和@PreDestroy注解的处理方法

品Spring:对@Resource注解的处理方法

品Spring:对@Autowired和@Value注解的处理方法

品Spring:真没想到,三十步才能完成一个bean实例的创建

品Spring:关于@Scheduled定时任务的思考与探索,结果尴尬了

 

作者是工作超过10年的码农,现在任架构师。喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。下面是公众号二维码,欢迎关注!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
蛋白质是生物体中普遍存在的一类重要生物大分子,由天然氨基酸通过肽键连接而成。它具有复杂的分子结构和特定的生物功能,是表达生物遗传性状的一类主要物质。 蛋白质的结构可分为四级:一级结构是组成蛋白质多肽链的线性氨基酸序列;二级结构是依靠不同氨基酸之间的C=O和N-H基团间的氢键形成的稳定结构,主要为α螺旋和β折叠;三级结构是通过多个二级结构元素在三维空间的排列所形成的一个蛋白质分子的三维结构;四级结构用于描述由不同多肽链(亚基)间相互作用形成具有功能的蛋白质复合物分子。 蛋白质在生物体内具有多种功能,包括提供能量、维持电解质平衡、信息交流、构成人的身体以及免疫等。例如,蛋白质分解可以为人体提供能量,每克蛋白质能产生4千卡的热能;血液里的蛋白质能帮助维持体内的酸碱平衡和血液的渗透压;蛋白质是组成人体器官组织的重要物质,可以修复受损的器官功能,以及维持细胞的生长和更新;蛋白质也是构成多种生理活性的物质,如免疫球蛋白,具有维持机体正常免疫功能的作用。 蛋白质的合成是指生物按照从脱氧核糖核酸(DNA)转录得到的信使核糖核酸(mRNA)上的遗传信息合成蛋白质的过程。这个过程包括氨基酸的活化、多肽链合成的起始、肽链的延长、肽链的终止和释放以及蛋白质合成后的加工修饰等步骤。 蛋白质降解是指食物中的蛋白质经过蛋白质降解酶的作用降解为多肽和氨基酸然后被人体吸收的过程。这个过程在细胞的生理活动中发挥着极其重要的作用,例如将蛋白质降解后成为小分子的氨基酸,并被循环利用;处理错误折叠的蛋白质以及多余组分,使之降解,以防机体产生错误应答。 总的来说,蛋白质是生物体内不可或缺的一类重要物质,对于维持生物体的正常生理功能具有至关重要的作用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值