高級開發(fā)竟然被構(gòu)造器循環(huán)依賴難住了?
是這樣的,有個(gè)實(shí)習(xí)生朋友問了我循環(huán)依賴的問題,我將Spring內(nèi)部的三級緩存原理都跟他說了,并保證Spring已經(jīng)解決了這個(gè)問題,然后他扔了一道題給我,說報(bào)錯(cuò)了。
好家伙,感情是想我讓我查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并沒有解決構(gòu)造器循環(huán)依賴
難道,spring對于這種循環(huán)依賴真的束手無策了么?
其實(shí)不是的,spring還有@Lazy這個(gè)大殺器...只需要我們對剛剛那兩個(gè)類小小的改造一下:
lazy為啥可以解決這個(gè)問題?
反調(diào)@Lazy注解可以看到
從源碼我們可以看到,對于@Lazy的依賴,我們其實(shí)是返回了一個(gè)代理類(以下稱為LazyProxy)而不是正真通過getBean拿到目標(biāo)bean注入。
而真正的獲取bean的邏輯,被封裝到了一個(gè)TargetSource類的getTarget方法中,而這個(gè)TargetSource類最終被用來生成LazyProxy了,那么我們是不是可以推測,LazyProxy應(yīng)該持有這個(gè)TargetSource對象。
而從我們懶加載的語意來講,是說真正使用到這個(gè)bean(調(diào)用這個(gè)bean的某個(gè)方法時(shí))的時(shí)候,才對這個(gè)屬性進(jìn)行注入/初始化。
那么對于當(dāng)前這個(gè)例子來講,就是說其實(shí)B創(chuàng)建的時(shí)候,并沒有去調(diào)用getBean("a")去獲取構(gòu)造器的參數(shù),而是直接生成了一個(gè)LazyProxy來做B構(gòu)造器的參數(shù),而B之后正真調(diào)用到A的方法時(shí),才會(huì)去調(diào)用TargetSource中的getTarget獲取A實(shí)例,即調(diào)用getBean("a"),這個(gè)時(shí)候A早就實(shí)例化好了,所以也就不會(huì)有循環(huán)依賴問題了。