深入理解 Spring 循环依赖之三级缓存(附源码分析)

前言:

学过 Spring 的都知道 Spring 利用三级缓存解决了循环依赖问题,那你知道什么是循环依赖?什么又是三级缓存?本篇将从源码层面分析 Spring 是怎么去利用三级缓存帮我们解决循环依赖问题。

深入理解 Spring IOC 底层实现机制(refresh 方法源码分析)

Spring 源码之 BeanDefinition 加载分析

深入理解 Spring Bean 生命周期(附源码分析)

什么是循环依赖?

简单来说 如果 Bean A 依赖了 Bean B,而 Bean B 又依赖了 Bean A,造成了相互依赖,也就是我们常说的的循环依赖,代码演示如下:

@Service
public class A{@Resourceprivate B b;
}@Service
public class B{@Resourceprivate A a ;
}

什么是三级缓存?

三级缓存是 Spring 为了解决循环依赖问题而设计的,在 Spring 源码中是三个 Map 存储,如下:

// 一级缓存 存放完整的Bean(实例化 初始化完成的 bean)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);// 二级缓存Map 存放不完整的Bean(只实例化完 还没属性赋值、初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);// 三级缓存Map 存放一个Bean的lambda表达式(也是我们常说的早期bean)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
  • 一级缓存(singletonObjects):用来存储成品 Bean 对象,也就是初始化完成可以直接被其他对象引用的对象。
  • 二级缓存(earlySingletonObjects):用来存储半成品对象,也就是实例化了但是还没有初始化的对象。
  • 三级缓存(singletonFactories):用来存储工厂对象,二级缓存就是从这个工厂中获取的对象,也就是一个 lambda 表达式,这个 lambda 表达式会提前暴露 Bean 对象的引用,执行这个 lambda 表达式会把这个引用放入二级缓存,同时删除三级缓存。

图解 Spring 三级缓存解决循环依赖:

我们用画图的方法来理解一下 Bean A 依赖 Bean B,Bean B又依赖 Bean A 的情况下,Spring 是怎么去进行 Bean A 和 Bean B 的加载的。

在这里插入图片描述

Spring 解决循环依赖的核心就是暴露早期对象,将对象的实例化和初始化分开。

循环依赖场景面试问题

源码分析部分较长,我们先把关于 Spring 三级缓存解决循环依赖的常见问题科普一下。

三个缓存对象的查找顺序是什么样子的?

这个问题阅读了源码的都知道是先找一级缓存,找不到再找二级缓存,最后才会找三级缓存。

一级缓存能否解决循环依赖问题?

不能,一级缓存和二级缓存存储的是不同类型的对象,如果只有一级缓存的话,那么成品对象和半成品对象会存放在一起,半成品状态的对象是直接接暴露给其他对象做引用的,放在一起就无法判断哪些是半成品对象那些事成品对象了。

二级缓存能否解决循环依赖问题?

可以解决某些情况下的循环依赖问题,但是有限制条件,不能出现代理对対象,例如 AOP 的场景、事务的场景。

为什么必须要使用三级缓存?

三级缓存的设计目的主要是针对 AOP 等需要代理场景的 Bean 对象,三级缓存是一个单例工厂,这个工厂的目的是延迟实例化阶段生成对象的代理,只有当 Bean 对象真正发生循环依赖时候,才会提前去生成代理对象,否则只会创建一个工厂放入三级缓存中,但是不会通过这个工厂去创建真正的对象,如果没有三级缓存的话,意味着所有 Bean 对象都需要在提前去生成代理对象,这违背了 Spring 的设计,Spring 设计之初是让 Bean 在生命周期的最后一步完成代理,而不是在实例化后就立马完成代理,Spring 结合 AOP 是通过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器来完成的,在这个后置处理的 postProcessAfterInitialization 方法中对初始化后的 Bean 完成 AOP 代理,如果出现了循环依赖,没有办法,只有给 Bean 先创建代理。

三级缓存为什么不能解决构造器引起的循环依赖?

因为构造器引起的循环依赖是发生在实例化阶段,而 Spring 使用一、二、三级缓存解决循环依赖的思想是把实例化和初始化分开,暴露早期实例化但是没有初始化的对象,构造器引起的循环依赖是发生在实例化阶段,自然就无法使用三级缓存来解决了。

Spring 三级缓存解决循环依赖源码分析

Spring 容器中的 Bean 获取是如下流程:

getBean–>doGetBean–>createBean–>doCreateBean
分析 Spring 使用三级缓存解决循环依赖,实际就是分析获取 Bean 的过程,当然这个过程包含创建 Bean,我们从 doGetBean 方法开始分析。

AbstractBeanFactory#doGetBean 方法源码分析

AbstractBeanFactory#doGetBean 方法在 Spring Bean 生命周期中分析过,本篇重点分析和三级缓存相关的点,比如 this.getSingleton(beanName) 方法。

//AbstractBeanFactory#doGetBean 方法
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {//根据 name 获取beanName 如果是别名会将别名转换为规范的名称String beanName = this.transformedBeanName(name);//根据beanName 从容器中获取 bean 对象(包含一级缓存 二级缓存 三级缓存)  本篇分析的重点Object sharedInstance = this.getSingleton(beanName);Object bean;//bean 不为空 直接从IOC 容器中获取 beanif (sharedInstance != null && args == null) {if (this.logger.isTraceEnabled()) {if (this.isSingletonCurrentlyInCreation(beanName)) {this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");} else {this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}//对 bean 进行 BeanFactory 的相关操作(不是本次分析的重点)bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);} else {//表示IOC 容器中没有当前单例 beanif (this.isPrototypeCurrentlyInCreation(beanName)) {//IOC 容器中正在创建原型bean 原型bean 中出现循环依赖直接抛出 bean 创建异常throw new BeanCurrentlyInCreationException(beanName);}//获取父级 BeanFactory  IOC 容器BeanFactory parentBeanFactory = this.getParentBeanFactory();if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {//当前IOC容器的父容器存在 且当前容器中不存在当前beanName的 beanDefinition//解析指定 Bean 名称的原始名称String nameToLookup = this.originalBeanName(name);//父IOC 容器是 AbstractBeanFactory if (parentBeanFactory instanceof AbstractBeanFactory) {//继续调用AbstractBeanFactory#doGetBean 方法查询单例beanreturn ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}if (args != null) {//委派父级IOC 容器根据指定名称和显式的参数查找return parentBeanFactory.getBean(nameToLookup, args);}if (requiredType != null) {//委派父级IOC 容器根据指定名称和类型去查找return parentBeanFactory.getBean(nameToLookup, requiredType);}//委派父级IOC 容器根据指定名称去查找return parentBeanFactory.getBean(nameToLookup);}//bean 是否需要做类型验证 一般不需要if (!typeCheckOnly) {//不需要做类型检查的时候 标记知道beanName 已经在创建中this.markBeanAsCreated(beanName);}try {//根据指定 beanName 合并 beanDefinition 主要是处理子类 bean 继承父类 bean 的公共属性问题RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);this.checkMergedBeanDefinition(mbd, beanName, args);//获取当前bean 的所有依赖bean 名称String[] dependsOn = mbd.getDependsOn();String[] var11;//当前bean 依赖的bean 是否为空 不为空 优先创建依赖beanif (dependsOn != null) {var11 = dependsOn;int var12 = dependsOn.length;for(int var13 = 0; var13 < var12; ++var13) {String dep = var11[var13];//是否存在循环依赖 存在就报异常if (this.isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}//注册各个bean 之间的关系 方便bean 销毁this.registerDependentBean(dep, beanName);try {//创建依赖的beanthis.getBean(dep);} catch (NoSuchBeanDefinitionException var24) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);}}}//是否是单例beanif (mbd.isSingleton()) {//创建bean 对象 并注册给所有依赖的bean 加入 Spring 一级缓存 并从二级 三级缓存中删除sharedInstance = this.getSingleton(beanName, () -> {try {//创建单例bean createBean 方法是重点方法return this.createBean(beanName, mbd, args);} catch (BeansException var5) {//创建失败显式地从单例缓存中删除实例this.destroySingleton(beanName);throw var5;}});//获取指定的bean实例对象bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) {//原型bean 每次都会创建一个新的对象var11 = null;Object prototypeInstance;try {//注册当前创建的原型bean 对象this.beforePrototypeCreation(beanName);//创建bean createBean 方法是重点方法prototypeInstance = this.createBean(beanName, mbd, args);} finally {//异常回调 告诉容器 指定的bean 不再创建this.afterPrototypeCreation(beanName);}//获取指定的bean实例对象bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);} else {//既不是单例bean 也不是原型bean 根据bean 定义的作用域来创建bean//获取bean的作用域(一般分为单例、原型、request、session、application、)String scopeName = mbd.getScope();//获取指定 bean 的作用域Scope scope = (Scope)this.scopes.get(scopeName);if (scope == null) {//bean 作用域为空 抛出异常throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {//匿名内部类 获取一个指定作用域的 beanObject scopedInstance = scope.get(beanName, () -> {//注册当前创建的bean 对象this.beforePrototypeCreation(beanName);Object var4;try {//创建bean createBean 方法是重点方法var4 = this.createBean(beanName, mbd, args);} finally {//异常回调 告诉容器 指定的bean 不再创建this.afterPrototypeCreation(beanName);}return var4;});//获取指定的bean实例对象bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} catch (IllegalStateException var23) {throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23);}}} catch (BeansException var26) {//bean 创建失败后的清理bean 工作this.cleanupAfterBeanCreationFailure(beanName);throw var26;}}//检查bean 的类型//类型不为空 且bean 不是当前类型if (requiredType != null && !requiredType.isInstance(bean)) {try {//获取bean 的类型转换器 并将类型转换为 requiredTypeT convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());} else {return convertedBean;}} catch (TypeMismatchException var25) {if (this.logger.isTraceEnabled()) {this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}} else {return bean;}
}

DefaultSingletonBeanRegistry#getSingleton 方法源码分析

DefaultSingletonBeanRegistry#getSingleton 方法主要是依次从 Spring 的一、二、三级缓存中获取 Bean 对象,需要注意的是容器启动时候第一次调用 doGetBean 的时候,从 Spring 的一、二、三级缓存中是获取不到 Bean 对象的,
我们继续往下看 this.createBean(beanName, mbd, args) 方法中的 this.doCreateBean(beanName, mbdToUse, args) 方法,doCreateBean 方法真正干活的方法。

//从 Spring IOC 容器中获取单例 bean 方法包装调用  没有实际操作
@Nullable
public Object getSingleton(String beanName) {return this.getSingleton(beanName, true);
}//从 Spring 三级缓存中获取单例 bean
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {//从一级缓存中获取 beanObject singletonObject = this.singletonObjects.get(beanName);//如果一级缓存中获取的bean 为空 且bean 正在创建中if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {//加同步锁synchronized(this.singletonObjects) {//从二级缓存中获取 beansingletonObject = this.earlySingletonObjects.get(beanName);//二级缓存中bean为空 且bean 允许早期引用if (singletonObject == null && allowEarlyReference) {//从三级缓存中获取bean(是lambda表达式)ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {//三级缓存中获取的bean 不为空 执行lambda 表达式 得到半成品的 beansingletonObject = singletonFactory.getObject();//将半成品的bean 放进二级缓存this.earlySingletonObjects.put(beanName, singletonObject);//将三级缓存的bean移出this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

AbstractAutowireCapableBeanFactory#doCreateBean方法源码分析

doCreateBean 在 Spring Bean 生命周期中分析过,本次重点关注 DefaultSingletonBeanRegistry#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)、AbstractAutowireCapableBeanFactory#populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)和
DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) 方法。

//AbstractAutowireCapableBeanFactory#doCreateBean
//真正创建Bean的方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {//实例bean 的包装对象 beanWrapper 其实就是对bean 的包装BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {//如果是单例bean 从 factoryBean 的缓存中移除当前bean (有可能在本Bean创建之前 就有其他Bean把当前Bean给创建出来了 比如依赖注入过程中)instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {//根据beanName、bean的定义信息、args创建一个新的实例对象  根据指定bean使用对应的策略创建新的实例 工厂方法 构造函数自动注入 简单实例化  底层使用 cglib代理 和 反射生成实例对象instanceWrapper = this.createBeanInstance(beanName, mbd, args);}//获取 instanceWrapper 包装的 bean 实例Object bean = instanceWrapper.getWrappedInstance();//获取 instanceWrapper 包装的 classClass<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}//加锁 保证线程安全synchronized(mbd.postProcessingLock) {if (!mbd.postProcessed) {try {//允许后置处理器修改合并后的 beanDefinition 将 工厂中的所有 MergedBeanDefinitionPostProcessors 应用到mbd  调用这些后处理器的 postProcessMergedBeanDefinition 方法this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);} catch (Throwable var17) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);}//标记该 beanDefinition 已经应用过 工厂中的所有 MergedBeanDefinitionPostProcessorsmbd.postProcessed = true;}}//判断是否需要暴露早期单例对象(解决循环依赖问题)//单例对象 允许循环引用 单例对象正在创建中 满足三个条件 则需要暴露早期单例对象boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);if (earlySingletonExposure) {//需要暴露早期单例对象if (this.logger.isTraceEnabled()) {this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");}//这里创建了一个匿名的ObjectFactory 工厂 可以用来获取对象//addSingletonFactory 方法会将这个 匿名的ObjectFactory 工厂 放入到 singletonFactories 中 singletonFactories 是 Spring 的三级缓存//为了避免循环依赖 尽早的持有对象的引用//本次重点关注的方法 三级缓存this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);});}//Bean对象的初始化 依赖注入在此触发//这个exposedObject在初始化完成之后返回作为依赖注入完成后的BeanObject exposedObject = bean;try {//bean 的属性填充 将bean 对象的实例封装 将Bean定义中配置的属性值赋值给实例对象 包括依赖的 bean this.populateBean(beanName, mbd, instanceWrapper);//bean 的初始化 包括应用工厂回调以及init方法和BeanPostProcessorsexposedObject = this.initializeBean(beanName, exposedObject, mbd);} catch (Throwable var18) {if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {throw (BeanCreationException)var18;}throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);}//第二次解决循环依赖问题if (earlySingletonExposure) {//需要暴露早期对象//从一级 二级缓存中获取bean 对象//把对象加入到二级缓存中本次重点分析的方法Object earlySingletonReference = this.getSingleton(beanName, false);if (earlySingletonReference != null) {//bean 对象不为空 根据名称获取的已注册的Bean和正在实例化的Bean是同一个if (exposedObject == bean) {//当前实例化的Bean初始化完成exposedObject = earlySingletonReference;} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {//当前Bean依赖其他Bean 并且当发生循环引用时不允许新创建实例对象//获取当前bean 依赖的其他beanString[] dependentBeans = this.getDependentBeans(beanName);//实际依赖的beanSet<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);String[] var12 = dependentBeans;int var13 = dependentBeans.length;//遍历依赖的beanfor(int var14 = 0; var14 < var13; ++var14) {String dependentBean = var12[var14];//删除给定Bean名称的单例实例 但仅当它没有用于类型检查之外的其他目的时才删除if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {//没有删除的bean 加入到实际依赖的bean 中actualDependentBeans.add(dependentBean);}}//因为bean创建后其所依赖的bean一定是已经创建的 actualDependentBeans不为空则表示当前的bean创建后其依赖的bean却没有全部创建完 说明勋在循环依赖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 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}try {//注册为一次性的bean 也就是处理Bean销毁前要执行的方法this.registerDisposableBeanIfNecessary(beanName, bean, mbd);return exposedObject;} catch (BeanDefinitionValidationException var16) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);}
}

DefaultSingletonBeanRegistry#addSingletonFactory 方法源码分析

DefaultSingletonBeanRegistry#addSingletonFactory 方法只做了一件事,就是把早期 Bean 对象放入三级缓存。

//添加单例bean对象到三级缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");//同步防止线程安全问题synchronized(this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {//三级缓存中不存在 bean 对象 bean 对象加入三级缓存 this.singletonFactories.put(beanName, singletonFactory);//从二级缓存中移出 bean 对象this.earlySingletonObjects.remove(beanName);//注册单例bean this.registeredSingletons.add(beanName);}}
}

AbstractAutowireCapableBeanFactory#populateBean方法源码分析

populateBean 方法就是给对象属性赋值,在 Spring Bean 生命周期中也分析过,本次我们主要分析 populateBean 方法中的 this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs) 这行代码,也就是 AbstractAutowireCapableBeanFactory#applyPropertyValues 方法。

//AbstractAutowireCapableBeanFactory#populateBean
//将bean 属性设置到生成的实例对象上
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {//BeanWrapper 为空 且有依赖的属性 直接抛异常if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}} else {//在设置属性之前 给任何InstantiationAwareBeanPostProcessors一个修改bean状态的机会if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {Iterator var4 = this.getBeanPostProcessorCache().instantiationAware.iterator();while(var4.hasNext()) {InstantiationAwareBeanPostProcessor bp = (InstantiationAwareBeanPostProcessor)var4.next();if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}//获取容器在解析Bean定义资源时为BeanDefiniton中设置的属性值PropertyValues pvs = mbd.hasPropertyValues() ? mbd.getPropertyValues() : null;//依赖注入的逻辑int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == 1 || resolvedAutowireMode == 2) {//MutablePropertyValues是PropertyValues具体的实现类MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs);if (resolvedAutowireMode == 1) {//ByNamethis.autowireByName(beanName, mbd, bw, newPvs);}if (resolvedAutowireMode == 2) {//ByTypethis.autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}// 获取是否有实现InstantiationAwareBeanPostProcessor的方法 这里主要使用postProcessProperties方法。boolean hasInstAwareBpps = this.hasInstantiationAwareBeanPostProcessors();// 获取是否需要进行依赖注入boolean needsDepCheck = mbd.getDependencyCheck() != 0;PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {// 属性为空 从beanDefinition中获取需要注入的信息pvs = mbd.getPropertyValues();}PropertyValues pvsToUse;for(Iterator var9 = this.getBeanPostProcessorCache().instantiationAware.iterator(); var9.hasNext(); pvs = pvsToUse) {//这里会调用AutowiredAnnotationBeanPostProcessor的 postProcessProperties()方法 会直接给对象中的属性赋值 真正的处理@Autowired @Resource @Value 等注解//AutowiredAnnotationBeanPostProcessor内部并不会处理pvs。直接返回自己设置的PropertyValues对象(可以在实例化前设置)。InstantiationAwareBeanPostProcessor bp = (InstantiationAwareBeanPostProcessor)var9.next();//这里的含义是程序员自己已经给属性赋值了,就不需要Spring给他赋值。这里是程序员没有赋值,所以需要Spring进行赋值pvsToUse = bp.postProcessProperties((PropertyValues)pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = this.filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}//设置值pvsToUse = bp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}}}//是否需要属性注入if (needsDepCheck) {if (filteredPds == null) {//为空 则初始化filteredPds = this.filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}//检查依赖this.checkDependencies(beanName, mbd, filteredPds, (PropertyValues)pvs);}if (pvs != null) {//应用给定的属性值 解决任何在这个bean工厂运行时其他bean的引用 必须使用深拷贝 所以我们不会永久地修改这个属性this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);}}
}

AbstractAutowireCapableBeanFactory#applyPropertyValues 方法源码分析

AbstractAutowireCapableBeanFactory#applyPropertyValues 方法很长,作用是应用给定的属性值,解决任何在这个 bean 工厂运行时其他bean的引用必须使用深拷贝,所以我们不会永久地修改这个属性,关于这些功能我们不需要要关注那么多,重点关注 Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue) 这行代码,我们接着看。

//应用给定的属性值 解决任何在这个bean工厂运行时其他bean的引用 必须使用深拷贝 所以我们不会永久地修改这个属性
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {//PropertyValues 是否有属性判断 没有属性不处理if (!pvs.isEmpty()) {//如果有安全控制 且 bw 是 BeanWrapperImpl 的实例if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {//设置 BW 安全上下文为访问控制山下文((BeanWrapperImpl)bw).setSecurityContext(this.getAccessControlContext());}//MutablePropertyValues 是 PropertyValues接口的默认实现 允许对属性进行简单操作 并提供构造函数来支持从映射 进行深度复制和构造等MutablePropertyValues mpvs = null;//原始属性值列表List original;//判断 pvs 是否是 MutablePropertyValuesif (pvs instanceof MutablePropertyValues) {//是就强转mpvs = (MutablePropertyValues)pvs;//mpvs 是否需要转换if (mpvs.isConverted()) {try {//设置属性值bw.setPropertyValues(mpvs);return;} catch (BeansException var18) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", var18);}}//获取 mpvs 属性列表original = mpvs.getPropertyValueList();} else {//获取 pvs 属性列表original = Arrays.asList(pvs.getPropertyValues());}//获取工厂自定义类型转换器TypeConverter converter = this.getCustomTypeConverter();if (converter == null) {converter = bw;}//BeanDefinition 解析BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, (TypeConverter)converter);//创建一个深拷贝List<PropertyValue> deepCopy = new ArrayList(original.size());//是否需要解析标志boolean resolveNecessary = false;//迭代遍历Iterator var11 = original.iterator();while(true) {while(var11.hasNext()) {//获取 PropertyValuePropertyValue pv = (PropertyValue)var11.next();if (pv.isConverted()) {//已经转换 直接加入到 深拷贝中deepCopy.add(pv);} else {//获取 pv 属性名称String propertyName = pv.getName();//获取 pv 属性值Object originalValue = pv.getValue();//是否 自动生成标记的规范实例if (originalValue == AutowiredPropertyMarker.INSTANCE) {//获取写方法(set)Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();if (writeMethod == null) {//写方法为空 抛出异常throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);}//将 writeMethod 封装到 DependencyDescriptor 中originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);}// valueResolver 根据 pv 解析出 originalValue 所封装的对象 重点方法(循环依赖的对象从这里进去)Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);//解析出来的值Object convertedValue = resolvedValue;//可转换标记 propertyName 是否 在bw中的可写属性 //prepertyName不是表示索引属性或嵌套属性boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);//是否可转换if (convertible) {//将 resolvedValue 转换为指定的目标属性对象convertedValue = this.convertForProperty(resolvedValue, propertyName, bw, (TypeConverter)converter);}//resolvedValue 与 originalValue 是否同一个对象if (resolvedValue == originalValue) {//是否可转换if (convertible) {// convertedValue 设置 给 PVpv.setConvertedValue(convertedValue);}//加入到 deepCopy 中deepCopy.add(pv);} else if (convertible && originalValue instanceof TypedStringValue && !((TypedStringValue)originalValue).isDynamic() && !(convertedValue instanceof Collection) && !ObjectUtils.isArray(convertedValue)) {pv.setConvertedValue(convertedValue);deepCopy.add(pv);} else {//标记为还需要解析resolveNecessary = true;//构建对象 加入到 deepCopy 中deepCopy.add(new PropertyValue(pv, convertedValue));}}}if (mpvs != null && !resolveNecessary) {//mpvs 不为空 且 不需要再解析 	将此 holder 标记为只包含转换后的值mpvs.setConverted();}try {//使用 deepCopy 构造一个新的 MutablePropertyValues 对象 设置到 bw 中以对 bw 的属性值更新bw.setPropertyValues(new MutablePropertyValues(deepCopy));return;} catch (BeansException var19) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", var19);}}}
}

BeanDefinitionValueResolver#resolveValueIfNecessary 方法源码分析

BeanDefinitionValueResolver#resolveValueIfNecessary 方法主要是对参数进行判断后继续调用方法。

//给定一个PropertyValue 返回一个值 必要时解析对工厂中其他bean的引用
@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {//是否是运行是 bean 引用if (value instanceof RuntimeBeanReference) {//循环依赖是运行时 bean 引用  循环依赖分析的重点RuntimeBeanReference ref = (RuntimeBeanReference)value;return this.resolveReference(argName, ref);} else if (value instanceof RuntimeBeanNameReference) {String refName = ((RuntimeBeanNameReference)value).getBeanName();refName = String.valueOf(this.doEvaluate(refName));if (!this.beanFactory.containsBean(refName)) {throw new BeanDefinitionStoreException("Invalid bean name '" + refName + "' in bean reference for " + argName);} else {return refName;}} else if (value instanceof BeanDefinitionHolder) {BeanDefinitionHolder bdHolder = (BeanDefinitionHolder)value;return this.resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());} else if (value instanceof BeanDefinition) {BeanDefinition bd = (BeanDefinition)value;String innerBeanName = "(inner bean)#" + ObjectUtils.getIdentityHexString(bd);return this.resolveInnerBean(argName, innerBeanName, bd);} else {//非重点 不分析}
}

BeanDefinitionValueResolver#resolveReference 方法源码分析

BeanDefinitionValueResolver#resolveReference 让我们看到熟悉的 getBean 方法,如果 Bean A 依赖了 Bean B,而 Bean B 又依赖了 Bean A,假设 Bean A 先创建,那这里的 getBean 方法就是去获取 Bean B,也就是重新走一遍 Bean A 的流程,循环依赖的循环来了。

//解析应用的属性值
@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {try {//获取 bean 类型Class<?> beanType = ref.getBeanType();Object bean;//引用对象是否在父容器中 在父容器中 就从父容器获取对象if (ref.isToParent()) {BeanFactory parent = this.beanFactory.getParentBeanFactory();if (parent == null) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean " + ref + " in parent factory: no parent factory available");}if (beanType != null) {bean = parent.getBean(beanType);} else {bean = parent.getBean(String.valueOf(this.doEvaluate(ref.getBeanName())));}} else {//不在父容器中String resolvedName;if (beanType != null) {NamedBeanHolder<?> namedBean = this.beanFactory.resolveNamedBean(beanType);bean = namedBean.getBeanInstance();resolvedName = namedBean.getBeanName();} else {//获取 resolvedName 引用包装的 bean 名称resolvedName = String.valueOf(this.doEvaluate(ref.getBeanName()));//获取 resolvedName 的bean 对象--此处重点来了 又看到 getBean 方法了 熟悉的感觉来了bean = this.beanFactory.getBean(resolvedName);}this.beanFactory.registerDependentBean(resolvedName, this.beanName);}if (bean instanceof NullBean) {bean = null;}return bean;} catch (BeansException var7) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, var7);}
}

DefaultSingletonBeanRegistry#getSingleton方法源码分析

DefaultSingletonBeanRegistry#getSingleton方法主要就是从三级缓存获取到对象,执行 lambda 表达式,并把得到的对象放入到二级缓存中,同时删除三级缓存的对象,DefaultSingletonBeanRegistry#getSingleton 方法中获取的 bean 对象和 DefaultSingletonBeanRegistry#addSingletonFactory 方法中的 Bean 对象的区别是两个方法之间是调用了 populateBean 方法的,也就是精力属性赋值,对象的属性值是有变化的。

//从缓存中获取 bean 对象
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {//从一级缓存中获取对象Object singletonObject = this.singletonObjects.get(beanName);//一级缓缓存中没有获取到对象且单例对象正在创建中if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {//同步防止线程安全问题synchronized(this.singletonObjects) {//从二级缓存中获取对象singletonObject = this.earlySingletonObjects.get(beanName);//二级缓存中对象为空且bean 对象允许暴露早期引用if (singletonObject == null && allowEarlyReference) {//从三级缓存中获取bean 对象(获取到的是lambda表达式)ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {//执行lambda表达式 也是回调 获取到 bean 对象singletonObject = singletonFactory.getObject();//bean 对象保存到二级缓存中(半成品对象)--这里的对象是经过了 populateBean 属性赋值的this.earlySingletonObjects.put(beanName, singletonObject);//从三级缓存中移出早期对象this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

至此,Spring 三级缓存相关源码已经分析完毕,希望可以帮助到有需要的小伙伴。

欢迎提出建议及对错误的地方指出纠正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/13213.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ | Leetcode C++题解之第89题格雷编码

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> grayCode(int n) {vector<int> ret(1 << n);for (int i 0; i < ret.size(); i) {ret[i] (i >> 1) ^ i;}return ret;} };

数据结构--红黑树(RBTree)

一、红黑树概念 1.1 什么是红黑树 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长…

自学VBA 设置单元格文字格式 笔记

一.设定对应单元格对应需要显示的格式 Cells(1, 1).Font.Size 18 字体大小 Cells(1, 2).Font.Color RGB(255, 0, 0) 字体颜色 Cells(1, 3).Font.Name "黑体" 字体类型 Cells(1, 4).Font.Italic True 字体斜体 Cells(1, 5).Font.FontStyle "BOLD"…

ubuntu下gcc编译器的安装

.gcc编译器的安装 一般linux下是覆盖含有的&#xff0c;如果没有执行更新命令 sudo apt update gcc安装成功&#xff0c;可以检查一下版本 可以看出我的gcc是9.4.0版本的

SpringBoot环境隔离Profiles

前言 通常我们开发不可能只有一个生产环境&#xff0c;还会有其它的开发&#xff0c;测试&#xff0c;预发布环境等等。为了更好的管理每个环境的配置项&#xff0c;springboot也提供了对应的环境隔离的方法。 直接上干货 知识点 激活环境方法 1&#xff0c;在application…

专用设备制造业供应商收发文件,有什么专业而轻便的方式吗?

专用设备制造业的特点包括&#xff1a;门类广、跨度大、科技含量高。它主要生产的是国民经济各部门&#xff08;包括采掘、化工、冶炼、能源、医疗卫生、环保等&#xff09;所急需的重大成套设备&#xff0c;例如矿产资源井采及露天开采设备、大型火电、水电、核电成套设备、石…

通过内网穿透免费部署我们的springboot+vue项目 实现跟服务器一样的效果

前文讲到通过内网穿透能够实现远程访问个人电脑的静态资源。本文将讲解通过内网穿透实现远程访问本地的项目&#xff0c;实现跟部署到服务器一样的效果&#xff1a;前文链接&#xff1a;通过内网穿透实现远程访问个人电脑资源详细过程&#xff08;免费&#xff09;&#xff08;…

深度学习之卷积神经网络理论基础

深度学习之卷积神经网络理论基础 卷积层的操作&#xff08;Convolutional layer&#xff09; 在提出卷积层的概念之前首先引入图像识别的特点 图像识别的特点 特征具有局部性&#xff1a;老虎重要特征“王字”仅出现在头部区域特征可能出现在任何位置下采样图像&#xff0c…

Python 小抄

Python 备忘单 目录 1.语法和空格 2.注释 3.数字和运算 4.字符串处理 5.列表、元组和字典 6.JSON 7.循环 8.文件处理 9.函数 10.处理日期时间 11.NumPy 12.Pandas 要运行单元格&#xff0c;请按 ShiftEnter 或单击页面顶部的 Run&#xff08;运行&#xff09;。 1.语法和空格…

6---Linux下版本控制器Git的知识点

一、Linux之父与Git的故事&#xff1a; Linux之父叫做“Linus Torvalds”&#xff0c;我们简称为雷纳斯。Linux是开源项目&#xff0c;所以在Linux的早期开发中&#xff0c;许多世界各地的能力各异的程序员都参与到Linux的项目开发中。那时&#xff0c;雷纳斯每天都会收到许许…

VMware Fusion 13.5.2 for Mac 发布,产品订阅模式首个重大变更

VMware Fusion 13.5.2 for Mac 发布&#xff0c;产品订阅模式首个重大变更 适用于基于 Intel 处理器和搭载 Apple 芯片的 Mac 的桌面虚拟化软件 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-fusion-13/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留…

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《集装箱海港级联物流-能源耦合系统协同优化方法 》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

FPGA - GTX收发器-K码 以及 IBERT IP核使用

一&#xff0c;前言 在FPGA - Xilinx系列高速收发器---GTX中详细介绍了GTX的基础知识&#xff0c;以及IP核的调用&#xff0c;下面将补充一下GTX在使用中的高速串行数据流在接收和发送时的控制与对齐&#xff08;K码&#xff09;&#xff0c;以及高速接口GTX&#xff0c;如果G…

Springboot开发 -- Postman 调试 session 验证 接口

当我们在开发Spring Boot应用时&#xff0c;经常会遇到带有Session验证的接口&#xff0c;这些接口需要用户先登录并获取到Session ID&#xff08;或称为cookie中的JSESSIONID&#xff09;&#xff0c;然后在后续的请求中携带这个Session ID来保持会话状态。下面我将以一个实际…

Hello, GPT-4o!

2024年5月13日&#xff0c;OpenAI 在官网正式发布了最新的旗舰模型 GPT-4o 它是一个 多模态模型&#xff0c;可以实时推理音频、视频和文本。 * 发布会完整版视频回顾&#xff1a;https://www.youtube.com/watch?vDQacCB9tDaw GPT-4o&#xff08;“o”代表“omni”&#xff0c…

高效协同,智慧绘制:革新型流程图工具全解析

流程图&#xff0c;作为一种直观展示工作过程和系统运作的工具&#xff0c;在现代办公和项目管理中发挥着不可或缺的作用。 其优势在于能够清晰、直观地呈现复杂的过程和关系&#xff0c;帮助人们快速理解并掌握关键信息。同时&#xff0c;流程图也广泛应用于各种场景&#xf…

【Python】图像批量合成视频,并以文件夹名称命名合成的视频

一个文件夹中有多个子文件夹&#xff0c;子文件夹中有多张图像。如何把批量把子文件夹中的图像合成视频&#xff0c;视频名称是子文件夹的名称&#xff0c;生成的视频保存到指定文件夹&#xff0c;效果记录。 代码 import os import cv2def create_video_from_images(image_f…

leetcode刷题(6):二叉树的使用

文章目录 104. 二叉树的最大深度解题思路c 实现 94. 二叉树的中序遍历解题思路c 实现 101. 对称二叉树解题思路c 实现 96. 不同的二叉搜索树解题思路c 实现 102. 二叉树的层序遍历解题思路c 实现 104. 二叉树的最大深度 题目: 给定一个二叉树 root &#xff0c;返回其最大深度…

ALV 图标显示

前言 在ABAP ALV中&#xff0c;使用fieldcat来定义列表中每个字段的显示属性&#xff0c;包括图标&#xff08;Icon&#xff09;的显示。图标可以在ALV列表中为特定列的行或标题添加图形元素&#xff0c;以增强视觉提示或传达附加信息。 ICON查询 图标的名称用事务码”ICON“进…

智能BI(后端)-- 系统异步化

文章目录 系统问题分析什么是异步化&#xff1f;业务流程分析标准异步化的业务流程系统业务流程 线程池为什么需要线程池&#xff1f;线程池两种实现方式线程池的参数线程池的开发 项目异步化改造 系统问题分析 问题场景&#xff1a;调用的服务能力有限&#xff0c;或者接口的…