Spring 项目中如何正确处理对象依赖
写在前面
目前,Spring 框架在互联网公司使用频繁。Spring 的核心功能之一就是 IOC(依赖注入)。但是每个人处理依赖注入的方式都不太一样,比如通过构造器注入、通过 Setter 方法注入等。到底如何选择呢?
内容核心
就我个人而言,我更喜欢使用 Setter 方法进行对象的注入。这种注入的方式可以避免循环依赖错误的发生。
循环依赖是什么
循环依赖其实是循环引用,也就是两个或两个以上的 bean 互相持有对方,形成闭环。比如:A 对象依赖 B 对象,B 对象依赖 C 对象,而 C 对象又依赖于 A 对象。
Spring 循环依赖场景主要有两种:
构造器的循环依赖(无法解决)
字段属性的循环依赖
如何解决循环依赖
Spring 中的单例对象的创建分为三个步骤:
第一步:createBeanInstance,通过 Bean 的构造方法实例化对象,并没有注入对象的属性
第二步:populateBean,进行实例对象属性的注入
第三步:initializeBean 调用 Spring 中的 init 方法,完成实例对象的创建。(xml 中的 init-method 属性中的所对应的方法;注解 @ PostConstruct 修饰的方法)
Spring 为解决循环依赖问题,使用了三级缓存。
singletonObjects:单例对象的 Cache
singletonFactories:单例对象工厂的 Cache,用于存储在 spring 内部所使用的 beanName->对象工厂的引用。
earlySingletonObjects:提前曝光的单例对象的 cache,用于存储在创建 Bean 早期对创建的原始 bean 的一个引用。
怎么使用这三级缓存,使用情况都集中于 DefaultSingletonBeanRegistry 类的 getSingleton(String beanName, boolean allowEarlyReference)方法中,具体代码流程如图:
那这么做为什么能解决 Spring 中的循环依赖问题
其实,在真正创建这个实例对象之前,这个对象已经产生了。只不过,当时只是通过构造方法进行实例化,并未对其属性进行注入,所以 Spring 提前将其曝光出来。比如:
对象 A 首先完成了初始化的第一步,并且将自己提前曝光到 singtonFactories 中,而此时要进行初始化的第二步的时候,发现自己依赖对象 B,而去尝试 get(B),发现 B 还未被创建,所以进行对象 B 的创建。B 在完成初始化第一步之后,发现自己依赖对象 A,尝试去 get(A),尝试一级缓存 singletonObjects 没有,二级缓存 earlySingletonObjects 也没有,尝试三级缓存 singletonFactories,由于 A 提前曝光到 singletonFactories 中,所以 B 能够通过 ObjectFactory.getObject()方法拿到对象 A,B 完成初始化过程之后,将自己放到 singletonObjects 中。此时,对象 A 获取到对象 B,顺利完成自己的初始化过程,进入一级缓存 singletonObjects 中。完成两个循环依赖对象的初始化。
总结
其实研究源码的目的,就是为了更好地解决系统中发生的问题。在编码过程中,提前将可能的问题处理掉。这样,在上线后,才不至于事故频发。
版权声明: 本文为 InfoQ 作者【技术小生】的原创文章。
原文链接:【http://xie.infoq.cn/article/2bf05f15dc5eb06f1265fd0c4】。文章转载请联系作者。
评论