循环依赖

public class Main {public static void main(String[] args) throws Exception {System.out.println(new A());}}class A {public A() {new B();}}class B {public B() {new A();}}

spring是如何解决循环依赖问题的?
singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects:提前曝光的单例对象的cache,存放尚未填充属性的原始bean对象
singletonFactories:单例对象工厂的cache,存放 bean 工厂对象
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {...// 从上至下 分表代表三级缓存private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);.../** Names of beans that are currently in creation. */// 这个缓存也十分重要:它表示bean创建过程中都会在里面待着,它在Bean开始创建时放值,创建完成时会将其移出~private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));/** Names of beans that have already been created at least once. */// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合不会重复,至少被创建了一次的,都会放进这里private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {...@Override@Nullablepublic Object getSingleton(String beanName) {return getSingleton(beanName, true);}@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}...public boolean isSingletonCurrentlyInCreation(String beanName) {return this.singletonsCurrentlyInCreation.contains(beanName);}protected boolean isActuallyInCreation(String beanName) {return isSingletonCurrentlyInCreation(beanName);}...}
以上代码的大致意思是:
使用spring是否就能高枕无忧了?
其实并不能,spring仅能解决单例模式下field属性注入(即setter方法注入)。我们来看以下三种循环依赖情况:
@Servicepublic class A {@Autowiredprivate B b;}@Servicepublic class B {@Autowiredprivate A a;}
@Servicepublic class A {public A(B b) {}}@Servicepublic class B {public B(A a) {}}
Caused by: 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.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@Servicepublic class A {@Autowiredprivate B b;public void test (){System.out.println("I am A");}}@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@Servicepublic class B {@Autowiredprivate A a;public void test (){System.out.println("I am B");}}@RestController@RequestMapping("/test")public class RecycleTestController {@Autowiredprivate A a;@GetMapping("/test")public void test(){a.test();a.toString();}}

你真的会用prototype作用域吗?
@Servicepublic class SingletonBean{@Autowiredprivate PrototypeBean prototypeBean;public void doSomething(){System.out.println(prototypeBean.toString());}}@Service@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class PrototypeBean{}
回想下本文章节2中的内容,我们简单分析一下:因为SingletonBean是单例的,所以在项目启动时就会初始化,prototypeBean本质上只是它的一个Property,那么ApplicationContex中只存在一个SingletonBean和一个初始化SingletonBean时创建的一个prototype类型的PrototypeBean。每次调用SingletonBean.doSomething()时,Spring会从ApplicationContex中获取SingletonBean,每次获取的SingletonBean是同一个,所以即便PrototypeBean是prototype的,但PrototypeBean仍然是同一个。每次打印出来的内存地址肯定是同一个。
明白了以上道理,这个问题的解决办法也就比较简单了。事实上,这种prototype作用域时我们不能简单的通过注入的方式注入一个prototypeBean,可以手动调用applicationContext.getBean("prototypeBean")方法每次获取的都是新的实例了。另外,还有个更优雅的写法,那就是我们注入的时候不注入真实实例,而是注入其代理对象,那么每次代理对象获取真实对象时,代理对象会自动帮我们new新的PrototypeBean实例。只需要在@Scope属性中增加一个proxyMode=ScopedProxyMode.TARGET_CLASS属性即可:
@Service@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode=ScopedProxyMode.TARGET_CLASS)public class PrototypeBean{}




