spring疑难解惑-循环依赖的解决 博客分类: spring spring循环引用
程序员文章站
2024-02-12 20:03:46
...
AbstractAutowireCapableBeanFactory#doCreateBean
DefaultSingletonBeanRegistry#getSingleton
AbstractAutoProxyCreator#
1.为什么要用三级缓存而不用二级缓存
一个设计良好的系统应该不会经常出现循环引用的情况的,在一开始就设置早期引用浪费系统资源;
2.为什么三级缓存中放入ObjectFactory,而不是bean
为了扩展,生成事务代理或其他代理,以便是代理也可以循环引用
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); //此处表示是否抛出早期引用,以便循环引用时使用,放在三级缓存的singtonFactories里面 //存在循环引用是采用调用ObjectFactory.getObject if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { //此处从二级缓存的earlySingtonObjects中获取,如何不为空表示,存在循环引用 //在doGetBean方法中,会将三级缓存的singtonFactories调用ObjectFactory.getObject放入到二级缓存的earlySingtonObjects中 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //如果相等表示,早期引用的bean(即设置到其他bean中的bean引用即为此处的早期引用) //因为exposedObject = earlySingletonReference;最终都是一个引用 //事务代理属于此类循环引用的实例,事务代理时在AbstractAutoProxyCreater的早期引用中创建的 if (exposedObject == bean) { exposedObject = earlySingletonReference; } //如果能够容忍创建的bean有多个版本,则可以设置allowRawInjectionDespiteWrapping=true,但这样就破换了spring的单例原则,不建议使用 //hasDependentBean此处表示是否存在引用创建中的bean的其他bean(姑且是beanOther),如果存在则beanOther引用了bean的早期引用,与最终生成的bean是不一致的,肯定不对 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { //此处的TypeCheckOnly=true的场景暂时没有想到待补充,也请路过的博友能够指点一二 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
DefaultSingletonBeanRegistry#getSingleton
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); // 该singletonFactory不为空,在doCreateBean阶段已经放入了ObjectFactory //此处调用getObject方法生成早期引用,并放入二级环境earlySingletonObjects if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
AbstractAutoProxyCreator#
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); //此处this.earlyProxyReferences中放入参与早期引用的缓存,在postProcessAfterInitialization中就不用再次创建了,保证了doCreateBean中exposedObject == bean //他们生成的代理功能本质上是一样的都是getAdvicesAndAdvisorsForBean获取的切面 //但仅限于AbstractAutoProxyCreator的子类生成的代理是可以循环引用的,例如事务属于此类 //其他类型的代理或自定义的代理可能没有实现earlyProxyReferences类似功能,导致exposed!=bean if (!this.earlyProxyReferences.containsKey(cacheKey)) { this.earlyProxyReferences.put(cacheKey, Boolean.TRUE); } return wrapIfNecessary(bean, beanName, cacheKey); } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.containsKey(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
1.为什么要用三级缓存而不用二级缓存
一个设计良好的系统应该不会经常出现循环引用的情况的,在一开始就设置早期引用浪费系统资源;
2.为什么三级缓存中放入ObjectFactory,而不是bean
为了扩展,生成事务代理或其他代理,以便是代理也可以循环引用
上一篇: 常用命令备注
下一篇: 远程连接mysql 授权方法详解