我的博客

spring的属性注入属于spring bean的生命周期一部分,bean的生命周期首先记住两个概念:

  1. spring bean:最终存在spring容器当中的对象
  2. 对象:实例化出来的对象,但是一个对象并不一定是spring bean

所谓的bean的生命周期就是磁盘上的类通过spring扫描,然后实例化,属性注入,跟着初始化,继而放到容器当中的大概过程

finishBeanFactoryInitialization

通过finishBeanFactoryInitialization初始化我们的单例非懒加载的Bean,创建一个Z.java来看看

首先实例化ApplicationContext容器对象

会调用构造方法里的refresh来初始化容器

执行完前后可以看到,在方法finishBeanFactoryInitialization里,Spring已经帮我们创建好了Bean,也就是bean的生命周期都在此方法内


通过此方法内的注释可以知道,接着会执行finishBeanFactoryInitialization#beanFactory.preInstantiateSingletons().doGetBean(beanName);方法

doGetBean

doGetBean方法内容有点多,这个方法非常重要,整个spring bean生命周期中这个方法有着举足轻重的地位,开始分析doGetBean的执行流程

第一个getSingleton

getSingleton(beanName) 是实现自动注入最主要的方法,spring初始化bean的时候先判断bean是否在容器当中,如果存在,直接返回,如不存在则生产。同时,这个方法也是API getBean(beanName) 的底层实现。
spring在初始化的时候容器当中肯定是没有Z的,为什么还需要去判断一下呢?因为一个bean被put到单例池(容器)的渠道有很多;除了spring容器初始化—扫描类----实例化-----put到容器这条线之外还有很多方法可以把一个对象put到单例池,比如:

这里是第一次从单例池中拿Z,肯定是null,所以 singletonObject == null 成立

判断Z是否在正在创建的集合里,第一次肯定是不在的,isSingletonCurrentlyInCreation == false 不成立

以上判断完成,会继续往下面走,继续实例化,然后下面又调用了一次getSingleton,上面也调用了一次getSingleton,两个getSingleton方法并不是同一个方法,这是方法重载,第二次getSingleton就会把bean创建出来

第二个getSingleton

第二次getSingleton就会把我们bean创建出来,换言之整个bean如何被初始化的都是在这个方法里面

当spring觉得可以着手来创建bean的时候首先便是调用beforeSingletonCreation(beanName); 判断当前正在实例化的bean是否存在正在创建的集合当中

当代码运行完this.singletonsCurrentlyInCreation.add(beanName)之后可以看到singletonsCurrentlyInCreation集合当中只存在一个Z,并且没有执行z的构造方法,说明spring仅仅是把Z添加到正在创建的集合当中,但是并没有完成bean的创建(因为连构造方法都没调用)

当运行完this.singletonsCurrentlyInCreation.add(beanName) 之后结果大概如下图这样

之后就会走到singletonObject = singletonFactory.getObject();这个调用的是createBean(beanName, mbd, args) 方法;把创建好的spring bean返回出来;至此第二次getSingleton方法结束

createBean

createBean()方法中调用了doCreateBean方法创建spring bean

doCreateBean

createBeanInstance:创建对象

运行完createBeanInstance之后控制台打印了Z的构造方法的内容,说明Z对象已经被创建了,但是这个时候的Z不是spring bean,因为spring bean的生命周期才刚刚开始

把前面知识串起来,画一下当前代码的语境

这个createBeanInstance方法是如何把对象创建出来的呢?大致流程是:

  1. 如果是自动装配,推断出来各种候选的构造方法
  2. 利用推断出来的候选构造方法去实例化对象
  3. 如果没有推断出合适的构造方法(或者没有提供特殊的构造方法),则使用默认的构造方法
  4. 利用默认的构造方法,使用反射去实例化对象

populateBean:属性自动注入

实例化完成后,开始执行方法populateBean完成属性自动注入,我们在Z里面添加自动注入X

首先看一下方法执行前的情况

没有执行populateBean之前只实例化了Z,X并没实例化;接下来看看执行完这行代码之后的情况

Z 填充 X (简称 zpx)首先肯定需要获取X,调用getBean(x),getBean的本质上文已经分析过,就是调用getSingleton(beanName),进入到第一次调用getSingleton。第一次getSingleton会从单例池获取一下X,如果X没有存在单例池则开始创建X,创建X的流程和创建Z一模一样,都会走bean的生命周期;比如把X添加到正在创建的bean的集合当中,推断构造方法,实例化X

{% qnimg Spring自动注入源码/17.png %}

这样就完成了X的自动注入

循环依赖

Z 填充 X,但是X对象里又自动注入Z

@Component
public class Z {@Autowiredprivate X x;public Z() {System.out.println("z create");}}
@Component
public class X {@Autowiredprivate Z z;public X() {System.out.println("x create");}}

是否能够获取到Z呢?首先我们想如果获取失败则又要创建z—>实例化z—填充属性----获取x----就无限循环了;所以结果是完成了循环依赖,所以这里肯定能够获取到Z,为什么呢?
联系上文第一次调用getSingleton是无法获取到Z的?因为上面说过第一次调用getSingleton是从单例池当中获取一个bean,但是Z显然没有完成生命周期(Z只走到了填充X,还有很多生命周期没走完)
所以应该是获取不到的?为了搞清楚这个原因得去查看第一次getSingleton的源码

总结:
首先spring从单例池当中获取Z,前面说过获取不到,然后判断是否在正在创建bean的集合当中,前面分析过这个集合现在存在Z和X;所以if成立进入分支;进入分支,spring直接从三级缓存中获取Z,根据前面的分析三级缓存当中现在什么都没有,故而返回null
进入下一个if分支,从二级缓存中获取一个ObjectFactory工厂对象;二级缓存中存在Z和X,故而可以获取到;跟着调用singletonFactory.getObject();拿到一个半成品的bean Z对象;然后把Z对象放到三级缓存,同时把二级缓存中Z清除(此时二级缓存中只存在一个X了,而三级缓存中多了一个Z)

问题:

  1. 为什么首先是从三级缓存中取呢?主要是为了性能,因为三级缓存中存的是一个Z对象,如果能取到则不去二级找了
  2. 为什么一开始不直接存三级缓存呢?如果直接存到三级缓存,只能存一个对象,假设以前存这个对象的时候这对象的状态为za,但是我们这里X要注入的z为zc状态,那么则无法满足
  3. 二级有什么用呢?为什么一开始要存工厂呢?但是如果存一个工厂,工厂根据情况产生任意za或者zb或者zc等等情况;比如说AOP的情况下z注入x,x也注入z,而x中注入的z需要加代理(AOP),但是加代理的逻辑在注入属性之后,也就是z的生命周期走到注入属性的时候z还不是一个代理对象,那么这个时候把z存起来,然后注入x,获取、创建x,x注入z,获取z;拿出来的z是一个没有代理的对象;但是如果存的是个工厂就不一样;首先把一个能产生z的工厂存起来,然后注入x,注入x的时候获取、创建z,x注入z,获取z,先从三级缓存获取,为null,然后从二级缓存拿到一个工厂,调用工厂的getObject();spring在getObject方法中判断这个时候z被AOP配置了故而需要返回一个代理的z出来注入给x,总之getObject会根据情况返回一个z,但是这个z是什么状态,spring会自己根据情况返回
  4. 为什么要remove二级缓存?如果存在比较复杂的循环依赖可以提高性能;比如x,y,z相互循环依赖,那么第一次x注入z的时候从二级缓存通过工厂返回了一个z,放到了三级缓存,而第二次y注入z的时候便不需要再通过工厂去获得z对象了.因为if分支里面首先是访问三级缓存;至于remove则是为了gc吧

initializeBean:初始化

InitializingBean 对应生命周期的初始化阶段,实例化和属性赋值都是Spring帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段.Aware方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用Aware接口获取的资源,这也是我们自定义扩展Spring的常用方式

这几种定义方式的调用顺序没有必要记.因为这几个方法对应的都是同一个生命周期,只是实现方式不同


参考:
博客

Spring自动注入原理相关推荐

  1. Java程序员进阶——Spring依赖注入原理分析

    Spring依赖注入原理分析 下面谈谈Spring是如何实现反转模式IOC或依赖注入模式DI: 平时,我们需要生成一个对象,使用new语法,如一个类为A public class A{public v ...

  2. 基于spring自动注入及AOP的表单二次提交验证

    2019独角兽企业重金招聘Python工程师标准>>> 这几天在网上闲逛,看到了几个关于spring的token二次提交问题,受到不少启发,于是自己动手根据自己公司的项目框架结构,制 ...

  3. spring依赖注入原理(转载)

    关于spring依赖注入原理的文章在网络上已经有很多,我要写的这篇文章原文出自http://taeky.iteye.com/blog/563450,只所以再一次写下来只是为了一为自己收藏,方便以后的复 ...

  4. Spring自动注入

    谈及一个问题,无非牵扯到三点,是什么,怎么来的,怎么用的 Spring自动注入是什么 是指容器中的一个组件中需要用到另一个组件(例如聚合关系)时,依靠spring容器创建对象,而不是手动创建: Spr ...

  5. 巧用 Spring 自动注入实现策略模式升级版

    一.前言 1.1 背景 在工作过程中,有时候需要根据不同的枚举(常量)执行不同的逻辑. 比如不同的用户类型,使用不同的优惠政策:不同的配置变化,走不同的处理逻辑等. 下面模拟一个根据不同用户类型,走不 ...

  6. spring依赖注入原理详解(转载)

    spring依赖注入原理详解----转载 所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中.当spring容器启动后,spring容器初始化,创建并管理bean对象,以及销毁它.所 ...

  7. 对不起,我就是喜欢问你Spring构造器注入原理

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:这样配置:让你的 IDEA 好用到飞起来文章转自:码农沉思录 我的小站:个人小站,欢迎小憩 作者:Static_ ...

  8. spring自动装配原理

    为了搞明白自动装配原理,需要知道spring容器管理bean的生命周期 Spring Bean 生命周期流程图 bean自身方法的生命周期 分为四步: //执行此段代码,spring容器的bean执行 ...

  9. Spring Setter注入原理

    本文内容如有错误.不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢.欢迎转载,转载请注明出处(https://blog.csdn.net/feng_xiaoshi/article/det ...

最新文章

  1. node中模块、AMD与CMD、ES6模块,node中使用ES6
  2. 小米Redmi 5G旗舰 K30 Pro,最大亮点:怼华为荣耀
  3. 计算机专业相关分类调研
  4. 使用vue-axios请求geoJson数据报错的问题
  5. SQL基础用法总结(以前复习的时候放在自己的新浪博客上)
  6. 如何设制 select 不可编辑 只读
  7. 财付通php接口,dedecms 财付通接口,dedecms财付通_PHP教程
  8. 深度学习——行数据的分拆及如何“喂”给训练过程
  9. angularjs笔记,基本指令,字符串反转,过滤器
  10. python sys.exc_info()详解
  11. LoadRunner压力测试实例
  12. Word - 修改界面语言和校对语言
  13. activex与matlab,基于ActiveX技术的LabVIEW与MATLAB混合编程总结
  14. SView三维轻量化浏览器
  15. pr文字转语音有插件吗_自媒体非常实用的文字转语音软件,配合PR软件实用简直绝配...
  16. Eclipse插件列表
  17. 电脑考证一级Word文档
  18. 整车控制器软件功能检测工装
  19. Word自动续表实现方法 不分表不手动加表头 一劳永逸实现续表 论文必备
  20. Jenkins集合git、maven,持续集成gitlab项目(二)

热门文章

  1. 笔记本计算机是什么意思啊,笔记本独立显卡什么意思_独立显卡是什么
  2. 张一鸣:10年面试2000人,我发现混的好的人,全都有同一个特质。
  3. SpreadJS 最新版 2021版本 下载试用
  4. vivo显示服务器无响应zenns,vivo iQOO 新机入手初体验
  5. PHP与出库单打印预览,包含下载与打印功能
  6. Jetson Xavier 刷机Jetpack4.3
  7. 微信公众号对接服务器教程,微信公众号开发--服务器配置(傻瓜入门教程)
  8. 微信小程序教程:文字跑马灯效果
  9. 华为 8 年女硕士离职感言,值得每个人思考
  10. mysql创建一个存储过程并创建定时任务