spring bean创建过程源码分析(上)

大家好,我是@zzyang(小卓),一个热爱技术的90后。这篇文章主要是带大家了解一下spring bean的生命周期,对spring bean的创建过程源码分析。由于篇幅有限,这里说的都是主干流程,至于一些细节和旁支我都注释和圈出来了,有兴趣自己翻源码,如果有问题、质疑或者想交流探讨技术,可联系我,我们一起探讨技术。

更多技术干货,请扫描下图二维码关注公众号。

spring bean创建过程源码分析(上)_第1张图片

本文是基于spring源码的5.1版本

在讨论spring创建bean的源码之前,我先大概介绍一下spring的ioc和aop的概念。

ioc(Inversion of Control,缩写为IoC)就是控制翻转的意思,简单来说就是你按照spring提供的配置bean的方式将bean的创建流程交给spring来完成,比如以xml的方式声明bean,以@Bean的注解声明bean,以@Componet注解方式声明bean,当你用这些方式来声明bean的时候,spring在启动的时候就知道要为这个类创建一个对象,接下来spring会按照自己的流程来一步一步完成bean的生成过程,也就是本文的主题,spring bean的创建流程。

aop(Aspect Oriented Programming)英文意思就是面向切面编程,说白了其实就是一个动态代理的过程,只不过spring将生成动态代理的过程给封装到框架的内部,开发者其实只需要声明一下对哪个对象的哪个方法进行代理(pointcut)和在被代理的方法该执行什么样的代码(advice),这样就实现了aop。

有时大家可能会很好奇怎么实现动态代理的过程,怎么我配置了一下切面,就给我代理了呢?我给大家简单解释一下,aop的实现离不开ioc,当在spring bean创建的过程,在某个环节(后面会说到)spring框架会去判断,你有没有配置过给创建的对象进行代理,怎么判断很简单,就是根据你配置的切点表达式判断一下,如果有就给你创建一个代理对象返回给你,这样你拿到的就是代理对象,接下来你对这个对象方法调用就会走你写的那个advice所对应的代码,如果判断没有,就会返回给你原来的对象,就这么简单。

大家不妨去了解一下静态代理,这会有助于大家了解动态代理,动态代理其实跟静态代理差不多,只不过静态代理需要你手动写对象的代理,属于硬编码的方式,有多少个类就得写多少个类的代理类,很麻烦,而动态代理是动态生成代理类,但是本质都是加一些特殊的功能,这里就不再过多赘述了。

好了说完了spring ioc和aop的基本概念之后,接下来就来进入spring ioc中的bean的创建流程源码分析。

一、bean的获取阶段

为什么先讲获取。因为spring源码中是先从ioc容器中获取对象,获取不到再创建的。所以这里先从doGetBean方法入手。

spring bean创建过程源码分析(上)_第2张图片

 spring bean创建过程源码分析(上)_第3张图片

 先根据getSingleton方法去获取对象 ,这里就牵扯出三级缓存解决循环依赖的问题.。

三级解决循环引用的问题
* 第一级缓存 singletonFactories ,这getObject的时候会去对bean创建一个代理对象
* 第二级缓存 earlySingletonObjects ,这里是存储早期对象
* 第三级缓存 singletonObjects ,成熟的,彻彻底底可用的实例

如果出现循环依赖的问题,这里就会获取到bean,只不过这个bean还没有被初始化,仅仅只是实例化出来的而已,如果需要被代理,这里其实也会被代理

假设没获取到,那么此时往下走else,尝试从父容器中获取bean

spring bean创建过程源码分析(上)_第4张图片

 二、bean的创建

父容器也没有,就要自己去创建这个对象,在创建之前合并BeanDefinition 和 注册依赖的bean,@DependsOn注解就是在这个阶段发挥作用的

spring bean创建过程源码分析(上)_第5张图片

 接下来就是对bean的作用返回进行判断,从这里可以看出,其实spring对于bean的作用范围中的单例和多例其实是采用硬编码的方式来进行完成的,其余的bean的作用范围,比如在web环境中的bean作用域session、springcloud环境中的@RefreshScope注解等都是通过扩展org.springframework.beans.factory.config.Scope的实现来完成的。大家有兴趣可以看看SessionScope和RefreshScope这两个实现类。

 spring bean创建过程源码分析(上)_第6张图片

 补充一点,肯定会有人好奇,我的代码明明没有动过,我的Controller一直在那,怎么做到的一个session一个Controller,其实原理很简单,就是你看见的Controller其实是个代理对象,每次调用的时候都会根据session的不同去重新创建一个新的真正的Controller对象去调用,这里涉及到spring aop的知识,有机会我们再讲。

接着我们顺着创建单例bean继续往下看,把创建单例bean的重要的每个环节都看一遍,从这我们就开始深入bean的生命周期源码阶段。从createBean方法开始

a. bean class 的加载阶段spring bean创建过程源码分析(上)_第7张图片

 因为可能是通过xml文件来声明bean的,所以要把bean class加载一下

b. bean实例化之前阶段

这个阶段主要是回调所有的InstantiationAwareBeanPostProcessor对象的postProcessBeforeInstantiation方法,这个阶段如果有返回对象,直接不走下面的生命周期了(因为返回值不为null,直接return了),所以一般没有人这么玩。

BeanPostProcessor组件体系

InstantiationAwareBeanPostProcessor,这个接口是BeanPostProcessor的子类,BeanPostProcessor接口及其衍生的接口(接下来我称为BeanPostProcessor组件)是bean生命周期中一个非常核心的类体系,因为spring bean在创建过程中不同的阶段都会回调BeanPostProcessor组件的方法,这样就可以达到扩展的目的。因为只要你自己实现了BeanPostProcessor组件,就可以在生命周期的不同阶段可以对你的bean进行不同的操作,达到自己的目的。比如说阿里开源的dubbo中@DubboReference注解(2.7.7版本推出的注解,取代@Reference注解,功能没有什么变化)在整合spring的过程中主要是通过ReferenceAnnotationBeanPostProcessor来实现的,这个接口就是BeanPostProcessor的实现。说实话,bean的生命周期一大部分都是通过BeanPostProcessor组件来完成扩展的。

我们继续往下看

spring bean创建过程源码分析(上)_第8张图片

 进入resolveBeforeInstantiation方法

spring bean创建过程源码分析(上)_第9张图片

 进入applyBeanPostProcessorsBeforeInstantiation方法

spring bean创建过程源码分析(上)_第10张图片

c . bean的实例化阶段

这个阶段是根据你的class对象,来创建一个实例对象出来。

进入doCreateBean方法

spring bean创建过程源码分析(上)_第11张图片

 进入createBeanInstance方法,对象就在这个方法创建的

spring bean创建过程源码分析(上)_第12张图片

 @Bean的构建方式、构造器注入创建对象的方式,这两个创建的细节就不研究了

spring bean创建过程源码分析(上)_第13张图片

 通过带构造参数的实例化构造方法来实例化我们就不看了。那么就进入instantiateBean方法

spring bean创建过程源码分析(上)_第14张图片

 从这里可以看出,不论怎么走,都是通过getInstantiationStrategy方法获取实例化对象的策略然后调用instantiate来实例化对象。点进getInstantiationStrategy方法会发现其实是获取的CglibSubclassingInstantiationStrategy,那么我们就进入instantiate方法

spring bean创建过程源码分析(上)_第15张图片

 这里我们可以看出,其实是获得了class的默认构造器,然后调用BeanUtils.instantiateClass(constructorToUse)来实例化对象,这是这内部就是简单的反射调用构造器创建对象。就不点进去了。

其实从这里我们可以看出,其实spring在创建对象实例的时候,最简单的方式其实就是通过反射直接通过调用的构造方法进行实例化。其实spring对象的实例化还有其他的方式,比如我上面图片标注的@Bean的构建方式、构造器注入创建对象的方式都不是走这。

在后面就是对创建创建出来的对象包装成BeanWrapper对象,直接返回。至此,bean的对象就被实例化出来了。

d. bean 的实例化之后阶段

接着往下看。

spring bean创建过程源码分析(上)_第16张图片

 这是一个很重要的一步,主要是为了解决循环依赖的,跟文章最前面说的解决循环依赖是能够相呼应上的。

接下来看populateBean方法 

spring bean创建过程源码分析(上)_第17张图片

spring bean创建过程源码分析(上)_第18张图片

 看看,这里就是继续回调BeanPostProcessor组件体系的方法,所以回调完就表明spring bean的创建阶段完成。至于这个阶段为什么叫spring的bean的实例化之后阶段,你可以看看回调的方法的名字,翻译过来就是后置处理在bean实例化之后,所以叫这个阶段。

这个方法回调完之后下面代码就是bean生命周期中又一个核心的阶段,那就是属性赋值阶段,什么@Autowired依赖注入之类的其实就是在下面代码给你完成的。但是你有没有发现,postProcessAfterInstantiation如果这个方法返回false,下面的代码就不会执行了,所以一般扩展也没有返回false的,没人这么玩。其实你可以发现,spring在bean的创建过程中提供了非常多的可扩展点,你可以在每个阶段改变bean的创建行为,虽然可能没有人去这么做,但是spring依然提供了这些点。其实这也是读源码的有趣的地方,你可以看见各种扩展点,自己就可以去使用扩展点,进行各种骚操作。

总结:

我们把这篇文章总结一下,最开始说讲了bean的获取,自己的容器获取不到就会从父容器获取,如果都没获取到就会自己创建。说创建之前,简单的说明了spring是如何通过三级缓存解决循环依赖的问题。创建的时候会根据bean的作用域不同,进行了不同的创建。接下来我们选择了深入单例bean的创建源码,进入了bean创建的生命周期阶段,bean class 的加载,bean的实例化阶段,详细分为实例化之前阶段、实例化阶段、实例化之后阶段,顺便插入了对BeanPostProcessor组件体系的讲解。至于spring bean的生命周期的其它阶段,比如属性赋值阶段,初始化阶段,我会再写一篇文章来讲述剩下的阶段。预知后事如何,就请听下回分解吧。谢谢大家。

你可能感兴趣的:(spring,java,后端)