手撕Spring后置处理器源码,彻底理解Spring核心

上篇回顾

承接上篇《手撕spring核心源码,彻底搞懂spring流程》。由于上下文之间的逻辑关系,没看过上篇的朋友强烈建议先看上篇。

简单对上篇做个总结:

首先咱们一起写了一个HelloWorld版本的Spring。因为Spring核心是控制反转,就是Bean对象都交给Spring来管理。开发者使用时只需要获取即可。在使用时分为两步:第一步,通过注解配置创建Spring上下文容器;第二步,从中获取需要的Bean来使用。

那讲道理就先把这两步需要的框架构造出来,于是先有了下面的架子:

eb8f9d4eafb64473e11793fe358afdbc.png

然后咱们要把这个架子填充血肉,达到真的可以通过注解来使用Spring,进行依赖注入的目标。那首先要把使用到的注解定义出来:

fd14eba8d222599dfb493e3e06b5ec9d.png

根据这些注解和定义好的框架来实现功能,这是一个有工作经验的开发人员都可以做到的事情。最终的代码为了让大家便于和Spring源码结合,我把名称稍微调整了一下,名称和Spring源码中一致,作用也一致:

0417ccb6715e17448dd28c6ef157d4a2.png

f981512b981d3f92c75a1aa229b52490.png

fe02f836737398cc2f75eb9034ac8085.png

spring创建Bean的重点步骤这里面体现了:

1、根据class生成Bean定义

2、根据Bean进行实例化

3、将实例的Bean属性进行填充(autowired的属性实例化)

4、完成Bean对象的创建

实际上spring在将Bean进行实例化到生成Bean对象之前还进行了很多的处理,比如说Bean初始化和Bean增强。AOP和Bean后置处理器有着密切的关系。今天就来重点说一下这部分。

本文的所有代码文字在 https://github.com/xiexiaojing/yuna 里可以找到。

Bean初始化

Spring初始化Bean有三种方式,原理是一样的,分别是:

1、实现InitializingBean接口

2、使用 @PostConstruct 注解

3、或者指定 init-method

<bean 
  id="testInitializingBean"
  class="com.TestInitializingBean"
  init-method="testInit">
 </bean>

他们同时存在时是有先后顺序的:

执行顺序:

构造器Constructor >

@PostConstruct >

InitializingBean >

init-method

由于上节大量实现了注解方式,三种初始化方式原理大同小异,本篇就手撕 InitializingBean 方式的代码:

先定义一个 InitializingBean 接口,直接从spring-beans的源码把接口定义复制过来即可:

f5e6333135d2dd8bd5b032960182f35b.png


然后找个使用场景,比如Bean实例化后要从远程获取值:

304363f958f27e341a518f4dc226bca8.png

实现也很简单,创建Bean之后加上一个判断+调用:

73a0a97b652d0be2e07d7752becc4fae.png


来看一下效果:

2147b17973bcb884e5ca6e045de7bbf8.png

以上就是Spring初始化过程的实现。

Bean的后置处理器

Spring中有两种后置处理器,分别为BeanFactoryPostProcessor和BeanPostProcessor。前者叫bean工厂后置处理器,后者叫bean后置处理器。现在咱们都在讲 Bean ,还没讲到 BeanFactory ,这里不做过多介绍。

后置处理器的作用好像是做木工。先把大体木雕的轮廓做出来,下一步还要进行微调及上色,就是再加工。Spring-AOP 就是通过后置处理器实现的。

那Bean后置处理器的执行时机是什么呢?看了接口定义就明白了。同样,咱们把Spring后置处理器的接口直接复制过来:

c43afbc5668121a3147649af99699bb4.png

这里面有两个方法,一个方法叫:

初始化前,后置处理

一个方法叫:

初始化后,后置处理

咱们就按照方法定义来实现:

e33ec0c620ee5e79dac1b8fd9520dd23.png

注意这里有个 postProcessorList 是我刚刚新定义的,因为Spring不只有一个后置处理器,来提供足够的扩展性:

9da8bf19809c811d2e5af9eb905d6e23.png


那Spring什么时候把后置处理器放到这个list里面的呢?扫描的时候放的,举个具体的后置处理器例子:

404fd584a9bc6a8f3d6a6cf8c1e93684.png


因为要被扫描到,那一定要是个Component。打印的那两句歌词,我经常用,所以说明一下出处:在《深入理解MQ生产端的底层通信过程-理解channel》里我有把歌曲原唱放上。大学的时候爱听,现在再听已经不觉得好听了。大概是发现结婚这十几年,老公得到了任性的爱情,自己却是那个万般包容的人心里不爽。哈哈哈,开个玩笑,像咱这种普通人,最终在一起的人都是相互包容,相互成就的。

放松了一下,回归正题,扫描后置处理器:

fc8318f726de524bfb78afb042203d43.png

因为后置处理器有严格定义,内部不能绑定Bean,不需要执行初始化,所以可以直接实例化。

运行结果:

1e77f0500f821f6dd48982f8362a5540.png

这里后置处理器的两个方法各运行了两次,是因为创建了两个Bean:

491524a32df0b970b83ccc0eaec06e36.png

当然,我们可以指定后置处理器的生效条件,可以不对所有的Bean生效。

这里简单引入一下 AOP ,我也不卖关子了。AOP是通过一个叫 

AnnotationAwareAspectJAutoProxyCreator

的后置处理器实现的。里面的很多实现就来判断自己可以代理哪些Bean的。

总结

再来总结一下spring创建Bean的重点步骤:

1、根据class生成Bean定义

2、根据Bean进行实例化

3、将实例的Bean属性进行填充

4、初始化Bean

5、Bean后置处理

6、完成Bean对象的创建

如果我把代码再规整一下,用Spring代码同样的方法命名,大家会发现与Spring源码很多地方已经很接近的。相信对Spring源码的理解是有帮助的。

好了,大家看完这篇文章建议再看一遍我之前的一篇文章:《把对象交给spring管理的3种方法及经典应用》,看看是否对Spring理解更深了?

编程一生

因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。

PDCA方法论,检查自己是否错过更新:每周三晚上8点左右,我都会更新文章,如果你没有收到,记得点开【编程一生】公众号找一下(*^▽^*)。

猜你喜欢

转载自blog.csdn.net/xiexiaojing/article/details/123785058
今日推荐