是这样的,有个实习生朋友问了我循环依赖的问题,我将Spring内部的三级缓存原理都跟他说了,并保证Spring已经解决了这个问题,然后他扔了一道题给我,说报错了。
好家伙,感情是想我让我查bug
你们看看?
- @Component
- public class A {
- private final B b;
- public A(final B b) {
- this.b = b;
- }
- public void print() {
- System.out.println("in a");
- }
- }
- @Component
- public class B {
- private final A a;
- public B(final A a) {
- this.a = a;
- }
- public void print() {
- System.out.println("in b");
- }
- }
- Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a' defined in file [C:\soft\code\common\MongodbDataTest\dbDataTest\target\test-classes\com\db\model\A.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b' defined in file [C:\soft\code\common\MongodbDataTest\dbDataTest\target\test-classes\com\db\model\B.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
- at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
- at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
- at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
- at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
- at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
- at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
- at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
- at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
- at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
- at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:782)
- at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:774)
- at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
- at org.springframework.boot.SpringApplication.run(SpringApplication.java:339)
- at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)
- at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
- at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
- ... 68 more
- Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b' defined in file [C:\soft\code\common\MongodbDataTest\dbDataTest\target\test-classes\com\db\model\B.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
这TM,原来Spring并没有解决构造器循环依赖
难道,spring对于这种循环依赖真的束手无策了么?
其实不是的,spring还有@Lazy这个大杀器...只需要我们对刚刚那两个类小小的改造一下:
lazy为啥可以解决这个问题?
反调@Lazy注解可以看到
从源码我们可以看到,对于@Lazy的依赖,我们其实是返回了一个代理类(以下称为LazyProxy)而不是正真通过getBean拿到目标bean注入。
而真正的获取bean的逻辑,被封装到了一个TargetSource类的getTarget方法中,而这个TargetSource类最终被用来生成LazyProxy了,那么我们是不是可以推测,LazyProxy应该持有这个TargetSource对象。
而从我们懒加载的语意来讲,是说真正使用到这个bean(调用这个bean的某个方法时)的时候,才对这个属性进行注入/初始化。
那么对于当前这个例子来讲,就是说其实B创建的时候,并没有去调用getBean("a")去获取构造器的参数,而是直接生成了一个LazyProxy来做B构造器的参数,而B之后正真调用到A的方法时,才会去调用TargetSource中的getTarget获取A实例,即调用getBean("a"),这个时候A早就实例化好了,所以也就不会有循环依赖问题了。