3. 代理对象与代理对象
代理对象与代理对象的循环依赖是怎么样的呢?解决过程又是如何呢?这里就留给小伙伴自己思考了,其实和普通Bean与代理对象是一模一样的,小伙伴想想是不是呢,这里我就不做分析了。
4. 普通Bean与工厂Bean
这里所说的普通Bean与工厂Bean并非指bean与FactoryBean,这将毫无意义,而是指普通Bean与FactoryBean的getObject
方法产生了循环依赖,因为FactoryBean
最终产生的对象是由getObject
方法所产出。我们先来看看栗子吧~
假设工厂对象A依赖普通对象B,普通对象B依赖普通对象A。
小伙伴看到这里就可能问了,诶~你这不对呀,怎么成了「普通对象B依赖普通对象A」呢?不应该是工厂对象A吗?是这样的,在Spring中,由于普通对象A是由工厂对象A产生,所有在普通对象B想要获取普通对象A时,其实最终寻找调用的是工厂对象A的getObject
方法,所以只要普通对象B依赖普通对象A就可以了,Spring会自动帮我们把普通对象B和工厂对象A联系在一起。
小伙伴,哦~
普通对象A
public class NormalBeanA { private NormalBeanB normalBeanB; public void setNormalBeanB(NormalBeanB normalBeanB) { this.normalBeanB = normalBeanB; } }
工厂对象A
@Component public class FactoryBeanA implements FactoryBean<NormalBeanA> { @Autowired private ApplicationContext context; @Override public NormalBeanA getObject() throws Exception { NormalBeanA normalBeanA = new NormalBeanA(); NormalBeanB normalBeanB = context.getBean("normalBeanB", NormalBeanB.class); normalBeanA.setNormalBeanB(normalBeanB); return normalBeanA; } @Override public Class<?> getObjectType() { return NormalBeanA.class; } }
普通对象B
@Component public class NormalBeanB { @Autowired private NormalBeanA normalBeanA; }
假设我们先创建对象A
由于FactoryBean和Bean的创建过程是一样的,只是多了步getObject
,所以我们直接定位到调用getObject
入口
if (mbd.isSingleton()) { // 开始创建bean sharedInstance = getSingleton(beanName, () -> { // 创建bean return createBean(beanName, mbd, args); }); // 处理FactoryBean bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // 先尝试从缓存中获取,保证多次从工厂bean获取的bean是同一个bean object = getCachedObjectForFactoryBean(beanName); if (object == null) { // 从FactoryBean获取对象 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } }
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { // 加锁,防止多线程时重复创建bean synchronized (getSingletonMutex()) { // 这里是Double Check Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { // 获取bean,调用factoryBean的getObject() object = doGetObjectFromFactoryBean(factory, beanName); } // 又从缓存中取了一次,why? 我们慢慢分析 Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; }else{ // ...省略初始化bean的逻辑... // 将获取到的bean放入缓存 this.factoryBeanObjectCache.put(beanName, object); } } }
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName){ return factory.getObject(); }
现在,就走到了我们自定义的getObject
方法,由于我们调用了context.getBean("normalBeanB", NormalBeanB.class)
,此时,将会去创建B对象,在创建过程中,先将B的早期对象放入三级缓存,紧接着填充属性,发现依赖了A对象,又要倒回来创建A对象,从而又回到上面的逻辑,再次调用我们自定义的getObject
方法,这个时候会发生什么呢?
又要去创建B对象…(Spring:心好累)
但是!此时我们在创建B时,是直接通过getBean
在缓存中获取到了B的早期对象,得以返回了!于是我们自定义的getObject
调用成功,返回了一个完整的A对象!
但是此时FactoryBean
的缓冲中还是什么都没有的。
// 又从缓存中取了一次 Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; }
这一次取alreadyThere
必然是null,流程继续执行,将此时将获取到的bean放入缓存
this.factoryBeanObjectCache.put(beanName, object);
从FactoryBean获取对象的流程结束,返回到创建B的过程中,B对象此时的属性也得以填充,再返回到第一次创建A的过程,也就是我们第一次调用自定义的getObject
方法,调用完毕,返回到这里
// 获取bean,调用factoryBean的getObject() object = doGetObjectFromFactoryBean(factory, beanName); Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere;
那么,此时this.factoryBeanObjectCache.get(beanName)
能从缓冲中拿到对象了吗?有没有发现,拿到了刚刚B对象填充属性时再次创建A对象放进去的!
所以,明白这里为什么要再次从缓存中获取了吧?就是为了解决由于循环依赖时调用了两次自定义的getObject
方法,从而创建了两个不相同的A对象,保证我们返回出去的A对象唯一!
怕小伙伴晕了,画个图给大家
工厂Bean与普通Bean.png
5. 工厂Bean与工厂Bean之间
我们已经举例4种循环依赖的栗子,Spring都有所解决,那么有没有Spring也无法解决的循环依赖问题呢?
有的!就是这个FactoryBean
与FactoryBean
的循环依赖!
假设工厂对象A依赖工厂对象B,工厂对象B依赖工厂对象A,那么,这次的栗子会是什么样呢?
普通对象
public class NormalBeanA { private NormalBeanB normalBeanB; public void setNormalBeanB(NormalBeanB normalBeanB) { this.normalBeanB = normalBeanB; } }
public class NormalBeanB { private NormalBeanA normalBeanA; public void setNormalBeanA(NormalBeanA normalBeanA) { this.normalBeanA = normalBeanA; } }
工厂对象
@Component public class FactoryBeanA implements FactoryBean<NormalBeanA> { @Autowired private ApplicationContext context; @Override public NormalBeanA getObject() throws Exception { NormalBeanA normalBeanA = new NormalBeanA(); NormalBeanB normalBeanB = context.getBean("factoryBeanB", NormalBeanB.class); normalBeanA.setNormalBeanB(normalBeanB); return normalBeanA; } @Override public Class<?> getObjectType() { return NormalBeanA.class; } }
@Component public class FactoryBeanB implements FactoryBean<NormalBeanB> { @Autowired private ApplicationContext context; @Override public NormalBeanB getObject() throws Exception { NormalBeanB normalBeanB = new NormalBeanB(); NormalBeanA normalBeanA = context.getBean("factoryBeanA", NormalBeanA.class); normalBeanB.setNormalBeanA(normalBeanA); return normalBeanB; } @Override public Class<?> getObjectType() { return NormalBeanB.class; } }
首先,我们开始创建对象A,此时为调用工厂对象A的getObject
方法,转而去获取对象B,便会走到工厂对象B的getObject
方法,然后又去获取对象A,又将调用工厂对象A的getObject
,再次去获取对象B,于是再次走到工厂对象B的getObject
方法…此时,已经历了一轮循环,却没有跳出循环的迹象,妥妥的死循环了。
我们画个图吧~
工厂Bean与工厂Bean.png
没错!这个图就是这么简单,由于始终无法创建出一个对象,不管是早期对象或者完整对象,使得两个工厂对象反复的去获取对方,导致陷入了死循环。
那么,我们是否有办法解决这个问题呢?
我的答案是无法解决,如果有想法的小伙伴也可以自己想一想哦~
我们发现,在发生循环依赖时,只要循环链中的某一个点可以先创建出一个早期对象,那么在下一次循环时,就会使得我们能够获取到早期对象从而跳出循环!
而由于工厂对象与工厂对象间是无法创建出这个早期对象的,无法满足跳出循环的条件,导致变成了死循环。
那么此时Spring中会抛出一个什么样的异常呢?
当然是栈溢出异常啦!两个工厂对象一直相互调用,不断开辟栈帧,可不就是栈溢出有木有~
6. 工厂对象与代理对象
上面的情况是无法解决循环依赖的,那么这个情况可以解决吗?
答案是可以的!
我们分析了,一个循环链是否能够得到终止,关键在于是否能够在某个点创建出一个早期对象(临时对象),而代理对象在doCreateBean
时,是会生成一个早期对象放入三级缓存的,于是该循环链得以终结。
具体过程我这里就不再细分析了,就交由小伙伴自己动手吧~
总结
以上我们一共举例了6种情况,通过分析,总结出这样一条定律:
在发生循环依赖时,判断一个循环链是否能够得到终止,关键在于是否能够在某个点创建出一个早期对象(临时对象),那么在下一次循环时,我们就能通过该早期对象进而跳出(打破)循环!
通过这样的定律,我们得出工厂Bean与工厂Bean之间是无法解决循环依赖的,那么还有其他情况无法解决循环依赖吗?
有的!以上的例子举的都是单例的对象,并且都是通过set方法形成的循环依赖。
假使我们是由于构造方法形成的循环依赖呢?是否有解决办法吗?
没有,因为这并不满足我们得出的定律
无法执行完毕构造方法,自然无法创建出一个早期对象。
假使我们的对象是多例的呢?
也不能,因为多例的对象在每次创建时都是创建新的对象,即使能够创建出早期对象,也不能为下一次循环所用!
好了,本文就到这里结束了,希望小伙伴们有所收获~
Spring IOC的核心部分到此篇就结束了,下一篇就让我们进行AOP之旅吧~