Spring源码-Bean循环依赖的解决【6】
实验环境:spring-framework-5.0.2、jdk8、gradle4.3.1
- Spring源码-IOC容器简介【1】
- Spring源码-IOC容器初始化过程【2】
- Spring源码-Xml Bean解析注册过程【3】
- Spring源码-自定义IOC容器及Bean解析注册【4】
- Spring源码-Bean实例化过程【5】
- Spring源码-Spring是如何解决Bean循环依赖的【6】
- Spring源码-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】
- Spring源码-AOP是如何实现代理的【8】
本文重点将一下Spring是如何解决循环依赖的,其核心逻辑在getSingleton方法中。 在正常情况下,该代码很普通,只是正常的检查下我们要拿的 bean 实例是否存在于缓存中,如果有就返回缓存中的 bean 实例,否则就返回 null,但这段代码对于Spring来说却是解决循环引用的核心代码。
解决循环引用逻辑:使用构造函数创建一个 “不完整” 的 bean 实例(之所以说不完整,是因为此时该 bean 实例还未初始化),并且提前曝光该 bean 实例的 ObjectFactory(提前曝光就是将ObjectFactory 放到 singletonFactories 缓存)。通过 ObjectFactory 我们可以拿到该 bean 实例的引用,如果出现循环引用,我们可以通过缓存中的 ObjectFactory来拿到 bean 实例,从而避免出现循环引用导致的死循环。这边通过缓存中的 ObjectFactory 拿到的 bean 实例虽然拿到的是 “不完整” 的 bean 实例,但是由于是单例,所以后续初始化完成后,该 bean实例的引用地址并不会变,所以最终我们看到的还是完整 bean 实例。
我们先看一下getSingleton方法执行流程
DefaultSingletonBeanRegistry#getSingleton方法
@Override @Nullable public Object getSingleton(String beanName) { return getSingleton(beanName, true); } /** * singletonObjects 缓存:beanName -> 单例 bean 对象。 * earlySingletonObjects 缓存:beanName -> 单例 bean 对象,该缓存存放的是早期单例 bean 对象,可以理解成还未进行属性填充、初始化。 * singletonFactories 缓存:beanName -> ObjectFactory。 * singletonsCurrentlyInCreation 缓存:当前正在创建单例 bean 对象的 beanName 集合。 * singletonObjects、earlySingletonObjects、singletonFactories 在这边构成了一个类似于 “三级缓存” 的概念。 */ @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1.【从一级缓存里面获取】从单例对象缓存中获取beanName对应的单例对象 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 2.加锁进行操作 synchronized (this.singletonObjects) { // 3.【从二级缓存里面获取】从早期单例对象缓存中获取单例对象 // 之所称成为早期单例对象,是因为earlySingletonObjects里的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作 singletonObject = this.earlySingletonObjects.get(beanName); // 4.如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用 if (singletonObject == null && allowEarlyReference) { // 5.【从三级缓存里面获取】从单例工厂缓存中获取beanName的单例工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 6.如果存在单例对象工厂,则通过工厂创建一个单例对象 singletonObject = singletonFactory.getObject(); // 7.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 8.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了, // 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂 this.singletonFactories.remove(beanName); } } } } return singletonObject; }
这里的代码还是比较简单的,就是依次从一、二、三级缓存里面获取bean,没有返回null。那么不禁有几个疑问?
1、bean实例是在什么时候放入三级缓存的?
bean实例化的时候,会调用doGetBean方法,方法里面会先调用getSingleton方法从缓存里面获取,获取不到才会调用createBean方法来创建bean。
在创建bean的时候,会先通过反射或cglib实例化一个早期bean对象(未进行属性注入的对象),然后调用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));将早期bean实例放入了三级缓存中(注意:这里放入的一个工厂方法匿名内部类,通过singletonFactory.getObject()获取bean实例,每调用一次就会执行一下getEarlyBeanReference方法)。之后是属性的注入与bean的初始化工作。
AbstractAutowireCapableBeanFactory#doCreateBean方法
/** * 真正创建bean的方法 */ protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // 封装被创建的bean对象 BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // TODO【重要】 这一步创建了bean实例,只是一个早期的对象,还没有填充属性值 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); // 获取实例化对象的类型 Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // 调用post-processors后置处理器 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // 向容器中缓存单例模式的bean,防止循环引用 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // TODO【重要】 向三级缓存添加bean实例 匿名内部类,防止循环引用,尽早持有对象的引用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 初始化bean实例,exposedObject在初始化完成之后返回依赖注入完成之后的bean Object exposedObject = bean; try { // TODO 【重要】 bean属性依赖注入,并且将bean定义中配置的属性值赋值给实例对象 populateBean(beanName, mbd, instanceWrapper); // TODO 【重要】开始初始化bean对象 调用Aware接口方法 -> 调用BeanPostProcessor.before方法 -> 调用init-method方法 -> 调用BeanPostProcessor.after方法 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) { // 获取指定名称的已注册的单例bean Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 根据名称获取已注册的bean和正在实例化的bean是同一个 if (exposedObject == bean) { // 当前实例化的bean初始完成 exposedObject = earlySingletonReference; } // 当前bean依赖其他bean,且当发送循环引用时,不允许创建新的实例对象 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { // 获取当前bean所依赖的其他bean String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { // 对依赖bean进行类型检查 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."); } } } } // 注册完成依赖注入的bean try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
2、bean实例是在什么时候放入二级缓存的?
其实就在DefaultSingletonBeanRegistry#getSingleton方法里面,从三级缓存获取到bean,然后放入二级缓存,再从三级缓存移除。
查看代码
// 6.【从三级缓存里面获取】从单例工厂缓存中获取beanName的单例工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 7.如果存在单例对象工厂,则通过工厂创建一个单例对象 singletonObject = singletonFactory.getObject(); // 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了, // 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂 this.singletonFactories.remove(beanName); }
3、bean实例是在什么时候放入一级缓存的?
在doGetBean方法里,方法里面会先调用getSingleton方法从缓存里面获取,获取不到才会调用createBean方法来创建bean,创建bean的时候会经历实例化、属性注入、初始化等步骤,此时bean已经是个完整的bean对象了,返回出去,调用addSingleton方法存入一级缓存,然后从二级、三级缓存移除。
DefaultSingletonBeanRegistry#getSingleton方法
/** * 通过singletonFactory创建bean实例,并存入一级缓存singletonObjects */ public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 如果当前工厂单例正在销毁,则不允许创建,抛出异常 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!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } // 校验是否已经在创建中,抛出异常 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { // 【在这里创建实例】singletonFactory创建bean实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { // TODO【重要】把bean实例加入缓存 addSingleton(beanName, singletonObject); } } return singletonObject; } }
4、只用一级缓存会有什么问题?
如果只用一级缓存,会把早期bean和完整的bean都放入一级缓存,里面有的bean还没有属性注入,但通过getBean方法能直接从一级缓存里面拿,太凌乱了,肯定是不行的。
5、缓存只用到两级会有什么问题?
假设我们只用两级缓存,早期的bean放入singletonFactories,完整的bean放入singletonObjects,不使用earlySingletonObjects,看看会有什么问题?
在bean实例化后会产生一个早期对象,Spring通过addSingletonFactory把早期bean放入singletonFactories(放入的是个匿名内部类),之后各种属性的依赖注入,在bean没有成为完整对象之前(也就是说没有放入singletonObjects之前),每次调用都会通过singleFactory.getObject()来获取bean实例,由于singleFactory.getObject()是个工厂方法,每次调用都会执行() -> getEarlyBeanReference(beanName, mbd, bean)这段代码。
我们再看getEarlyBeanReference方法里面是怎么处理的,如果是普通的bean会返回它本身,如果bean被代理了则会创建bean的代理对象。
看到这里好像也没说问题,但问题就出现在这里。当发现bean符合代理条件,会通过wrapIfNecessary创建一个代理对象。由于singleFactory.getObject()是个工厂方法,也就是说每调用一次都会创建一个新的代理对象,违法了bean的单例性。参考:Spring源码-AOP部分-Spring是如何对bean实现AOP代理的
所以Spring借助了earlySingletonObjects来保证bean的单例。第一次调用时肯定会走到singleFactory.getObject()获取bean(普通bean或代理bean),然后把它放到earlySingletonObjects,之后直接从earlySingletonObjects里面获取,这样就保证了singleFactory.getObject()只执行一次。
总之,当bean是普通对象时,两级缓存是没有问题的,因为getEarlyBeanReference无论执行几次都会返回这个bean本身。但当bean是代理对象时,每执行一次就会创建一个新的代理对象,违法了单例性。所以需要借助earlySingletonObjects来保证bean的单例,就构成了现在的三级缓存。
最后当bean成为了完整对象,就放入singletonObjects。
// 向三级缓存添加bean实例 匿名内部类,防止循环引用,尽早持有对象的引用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); // 获取提前访问指定bean的引用,通常用于解析循环引用。 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; // 有可能返回代理对象,并不是原来的bean exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }
@Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { this.earlyProxyReferences.add(cacheKey); } return wrapIfNecessary(bean, beanName, cacheKey); } protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 判断是否应该代理这个bean if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } /** * 判断是否是一些InfrastructureClass或者是否应该跳过这个bean * InfrastructureClass是指Advice、PointCut、Advisor等接口的实现类 */ if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. // 获取这个bean的通知 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 创建代理 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
详细证明请看下篇文章: Spring源码-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】
本文来自博客园,作者:wzyy,转载请注明原文链接:https://www.cnblogs.com/wwzyy/p/15859833.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix