循环依赖是指两个或者多个Bean之前相互持有对方。在Spring中循环依赖一般有三种方式:
创新互联服务项目包括镜湖网站建设、镜湖网站制作、镜湖网页制作以及镜湖网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,镜湖网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到镜湖省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!构造函数循环依赖
setter方法循环依赖
prototype 范围的依赖处理
构造函数循环依赖
在Spring中构造函数循环依赖是无法解决的,因为构造函数依赖其实是方法间循环调用的一种,会发生死循环。但是在Spring中会直接抛出BeanCurrentlyInCreationException
异常。源码如下:
// 在缓存中获取Bean,如果没有就创建Beanpublic Object getSingleton(String beanName, ObjectFactory> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { // 在缓存中获取Bean Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 判断容器是否正在销毁单实例Bean if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } // 将当前需要创建的Bean标示放到Set集合,如果失败则抛出BeanCurrentlyInCreationException异常 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet(); } try { // 创建Bean实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } ... if (newSingleton) { // 将Bean实例注册到singletonObjects容器中 addSingleton(beanName, singletonObject); } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }protected void beforeSingletonCreation(String beanName) { // 将当前需要创建的Bean标示方法Set集合,如果失败则抛出BeanCurrentlyInCreationException异常 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
执行过程:
从缓存中获取Bean,如果没有则走创建Bean流程
判断容器是否正在销毁单实例Bean,如果是则不创建Bean
将当前需要创建的Bean标示(name)放入Set集合中(当前正在创建的Bean池),如果放入失败则抛出
BeanCurrentlyInCreationException
异常创建Bean实例
将Bean实例注册到容器(放到缓存中)
解决构造函数依赖主要是第3步实现的,Spring在容器创建的Bean的时候,会将Bean的标示(name)放到一个Set集合里面(当前正在创建的Bean池)。当在创建Bean的过程中,发现自已经在这个Set集合中时,就直接会抛出
BeanCurrentlyInCreationException
异常,而不会发生死循环。
setter方法循环依赖
@Servicepublic class AService { @Autowired private BService bService; @Autowired public void setbService(BService bService) { this.bService = bService; } }
这两种方式都算是setter方法依赖。我们创建单实例Bean的大致过程可以划分成三个阶段:
实例化
createBeanInstance(beanName, mbd, args);
设置属性值
populateBean(beanName, mbd, instanceWrapper);
初始化
initializeBean(beanName, exposedObject, mbd);
对于Setter注入造成的循环依赖,Spring容器是在创建Bean第一步实例化后,就将Bean的引用提前暴露出来。通过提前暴露出一个单例工厂方法,从而使得其他Bean可以引用到该Bean。
创建Bean时提前暴露刚完成第一步的Bean,源码如下:
addSingletonFactory(beanName, new ObjectFactory
自动装配过程中获取单实例Bean,源码如下:
protected 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 != NULL_OBJECT ? singletonObject : null); }
我们可以看到,在自动装配Bean的过程中,会去找三个缓存:
singletonObjects:存放完成创建的Bean所有步骤的单实例Bean
earlySingletonObjects:存放只完成了创建Bean的第一步,且是由单实例工厂创建的Bean
singletonFactories:存放只完成了创建Bean的第一步后,提前暴露Bean的单实例工厂。http://www.chacha8.cn/detail/1132398214.html
这里为什么会用一个三级缓存呢,为啥不直接将只完成了创建Bean的第一步的Bean直接方到
earlySingletonObjects
缓存中呢?我们跟入
getEarlyBeanReference(beanName, mbd, bean)
我们可以发现,单实例工厂方法返回Bean的时候还执行了后置处理器的,在后置处理器中我们还可以对Bean进行一些特殊处理。如果我们直接将刚完成实例化的Bean放入earlySingletonObjects
缓存中,那么失去对Bean进行特殊处理的机会。
源码如下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { return null; } } } } return exposedObject; }
对于
singleton
作用域 bean ,可以通过setAllowCircularReferences(false);
来禁用循环引用。郑州不育不孕医院:http://www.zzchyy110.com/
prototype 范围的依赖处理
对于prototype
作用域 bean, Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓存prototype
作用域的 bean ,因此无法提前暴露一个创建中的 bean 示。
另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
网站名称:Spring源码(八)循环依赖-创新互联
网站URL:http://scpingwu.com/article/dcejhe.html