IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring三级缓存 -> 正文阅读

[Java知识库]Spring三级缓存


循环依赖

循环依赖如下图所示:

在这里插入图片描述
对应的spring代码形式如下:

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private C c;
}

@Component
public class C {
    @Autowired
    private A a;
}

又或者:
在这里插入图片描述
对应的spring代码形式如下:

@Component
public class A {
    @Autowired
    private A a;
}

上面展示的循环依赖都是Spring可以解决的,但是对于构造器的循环依赖注入,Spring无法解决,会抛出异常:

@Component
public class A {
    private B b;

    public A(B b) {
        this.b = b;
    }
}

@Component
public class B {
    private A a;

    public B(A a) {
        this.a = a;
    }
}

对于Prototype类型的bean来说,如果存在循环引用也是会直接抛出异常结束

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class A {
    @Autowired
    private B b;
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class B {
    @Autowired
    private A a;
}


如何解决循环依赖

在正式研究Spring如何解决循环依赖之前,不如我们就假设spring目前没有提供三级缓存来解决循环依赖,那么目前spring的getBean流程图就如下所示:

在这里插入图片描述

getBean总共就三个大的阶段:

在这里插入图片描述
对于Spring而言,循环依赖会发生在第二步,即属性注入的过程,因此我们要想办法在属性注入前将当前Bean依赖的其他bean都进行创建。

在属性注入环节,如果发现当前bean依赖于其他bean,那么会去创建对应的bean.

不妨思考下面这个流程:

  • A实例化完毕后,进行属性注入环节,发现其依赖于B,于是去创建B
  • 进入B的getBean流程,B创建完后,进行属性赋值环节,又发现B又依赖于A,因此又尝试去创建A ---->死循环就出现在这里

大家可以思考一下,如何解决上面这个死循环.

  • 当B调用getBean(A)的时候,因为此时A已经创建出来了,只是还没有赋值,那么是不是可以用一个集合记录当前已经实例化好的bean集合,并且将这个已经创建好的A实例放到这个提前暴露bean实例的缓存中。
  • 这样当B调用getBean(A)的时候,该方法一开始还是去查单例缓存池,如果该缓存池没有就再去查询提前暴露bean实例缓存池,如果有了,那么直接返回即可。

在这里插入图片描述

这里提前暴露的意思就是将已经实例化bean但是还没有赋值的bean实例放到一个缓存池中,可以让其他bean在需要当前bean,但是当前bean还没初始化完的情况下,先引用这个暴露的bean

注意: 暴露出去的是这个bean对象的引用,因此该bean后续的属性注入的修改都是在同一块内存上完成的。

添加了提前暴露的缓存池后,循环引用的流程就变成了下面这样子:

  • getBean(A),先去查一下单例缓存和提前暴露的缓存池有无此bean
  • A第一次创建是没有的,那么我直接去实例化A,然后放入提前暴露的缓存池中
  • 进入A的属性赋值阶段,发现依赖B,然后去getBean(B)
  • getBean(B),先去查一下单例缓存和提前暴露的缓存池有无此bean
  • B第一次创建是没有的,那么我直接去实例化B,然后放入提前暴露的缓存池中
  • 进入B的属性赋值阶段,发现依赖A,然后去getBean(A)
  • getBean(A),先去查一下单例缓存和提前暴露的缓存池有无此bean
  • 发现提前暴露的缓存池中有A的引用,直接返回
  • B进入初始化流程,然后创建完毕,返回
  • A进入初始化流程,然后创建完毕,返回

还有一点大家可以看出来,因为必须在实例化后,当前bean对象才会被提前放入缓存池中,因此构造器造成的循环依赖无法解决

可以看到此时循环引用的问题就已经被解决了,但是我们给出的解决方案还存在诸多问题,但是思路是正确的,那么下面来看看Spring是如何完美解决bean的循环依赖的吧。


三级缓存解决循环依赖

我们上面只使用了二级缓存,即一个单例缓存池和一个提前暴露的单例缓存池,但是spring在此基础上多加了一个缓存池,并且具体的使用上也和我们上面讲的有点区别:

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); // 三级缓存
	...
	
	//用来记录正在创建的单例bean有哪些,当bean创建完毕后,会从该集合中移除
	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	//标记已经创建完毕的bean有哪些
	private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}

三级缓存的作用分别如下:

  • singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  • earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean对象(尚未填充属性),用于解决循环依赖
  • singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

下面跟随我的脚本一起来看看getBean过程中三级缓存是如何发挥作用并巧妙解决循环依赖问题的吧:


三级缓存解决流程

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
		String beanName = transformedBeanName(name);
		Object beanInstance;

		//查询三级缓存
		Object sharedInstance = getSingleton(beanName);
		//如果缓存中存在,就直接返回
		if (sharedInstance != null && args == null) {
			 ...
			//对FactoryBean的返回值做处理
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			//如果当前beanName是Prototype并且正处于创建状态,那么这里直接抛出异常
			//Prototype类型的bean不能被循环引用的体现
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			//先去父IOC找找有无该bean定义,有的话直接返回
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {...}

			if (!typeCheckOnly) {
			//标记当前bean创建完毕
			//加入alreadyCreated集合
				markBeanAsCreated(beanName);
			}

			StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
					.tag("beanName", name);
			try {
			    //如果传入的bean设置了需要返回的类型的话--返回bean的时候需要进行类型转换
				if (requiredType != null) {
					beanCreation.tag("beanType", requiredType::toString);
				}
				//返回合并后的bean定义---主要对存在继承关系的beanDefinition进行处理
				//就是在使用bean标签时,我们可以指定某个bean为抽象bean,然后该抽象bean里面放置一些公共配置
				//其他bean会引用该bean作为其父bean,从而继承这些抽象配置,避免重复书写
				//这里就是处理这种情况---还有就是判断当前bean定义的scope是否设置了,如果没有设置,默认为单例
				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				//检查一下bean的定义是不是抽象的,抽象bean定义不能被实例化
				checkMergedBeanDefinition(mbd, beanName, args);
				
				//如果设置了beanDefintion的dependon属性,那么这里会进行处理
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
	               ....      
				}

				//下面进行bean的创建流程
				
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
						//创建单例Bean
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
						//创建失败,销毁该单例bean
							destroySingleton(beanName);
							throw ex;
						}
					});
					//对FactoryBean的返回值做处理
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				//其他类型的scope的bean的创建流程
                ....
           }
           //返回前尝试进行类型转换--->requiredType设置的话
		return adaptBeanInstance(name, beanInstance, requiredType);
	}

getMergedLocalBeanDefinition方法虽然与本文主题没多大关系,但是因为这个方法还是蛮有用的,我还是忍不住想拿出来分析一下:

	protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		//mergedBeanDefinitions集合中存放的是已经合并好bean定义的所有RootBeanDefinition
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		//stale表示当前bean是否需要进行合并检查---一开始为true,检查完就false了
		//这里既然都存在于集合里面了,一般情况下都是检查过的,因此直接返回(手动搞进去的,除非自己设置一下)
		if (mbd != null && !mbd.stale) {
			return mbd;
		}
		//进行合并检查
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}

获取当前beanName对应的解析好的beanDefinition

	@Override
	public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
	//去beanDefinitionMap中找,找不到抛出异常
		BeanDefinition bd = this.beanDefinitionMap.get(beanName);
		if (bd == null) {
			if (logger.isTraceEnabled()) {
				logger.trace("No bean named '" + beanName + "' found in " + this);
			}
			throw new NoSuchBeanDefinitionException(beanName);
		}
		return bd;
	}

我们通过registerBeanDefinition方法注册进来的bean定义,都是放入了beanDefinitionMap集合中,这点需要明白

	protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
			throws BeanDefinitionStoreException {
		return getMergedBeanDefinition(beanName, bd, null);
	}
protected RootBeanDefinition getMergedBeanDefinition(
//第三个参数这里先忽略--本文不讲解
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {
         //bean检查合并完之后,肯定是要放入缓存,即mergedBeanDefinitions中的,因此防止这里出现并发问题
         //就加锁了
		synchronized (this.mergedBeanDefinitions) {
			RootBeanDefinition mbd = null;
			RootBeanDefinition previous = null;

			//这里再次判断缓存中是否已经存在了---双重锁
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}
			if (mbd == null || mbd.stale) {
				previous = mbd;
				//getParentName()方法返回null,说明当前bean不存在继承某个抽象bean的情况
				if (bd.getParentName() == null) {
					//直接转换为RootBeanDefinition即可
					if (bd instanceof RootBeanDefinition) {
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
					// 走到这里说明当前bean继承了抽象bean,那么需要将抽象bean的配置信息合并到该子bean上
					BeanDefinition pbd;
					try {
					//拿到抽象bean的beanName
						String parentBeanName = transformedBeanName(bd.getParentName());
						//如果父bean的名字和自己不一样
						if (!beanName.equals(parentBeanName)) {
						//获取到父类合并后的定义---因为防止父类也继承其他bean
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
						   //如果父bean名字和自己一样--那么就去父IOC寻找--如果没有设置父IOC就直接抛出异常了
						   //或者如果父IOC解析也得不到这个bean定义,也抛出异常
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
												"': cannot be resolved without a ConfigurableBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
					}
					// Deep copy with overridden values.
					//先复制一份父bean的所有属性
					mbd = new RootBeanDefinition(pbd);
					//然后再覆盖掉自己重写后的相关属性
					mbd.overrideFrom(bd);
				}

				//如果当前bean定义没有设置作用域,默认为单例
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(SCOPE_SINGLETON);
				}

				// 这里暂时忽略
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}

				//放入缓存
				if (containingBd == null && isCacheBeanMetadata()) {
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
			if (previous != null) {
				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
			return mbd;
		}
	}

之所以带大家看看getMergedBeanDefinition方法的实现,原因在于让各位明白,我们通过registerBeanDefinition方法注册进来的BeanDefinition是存放在beanDefinitionMap集合中。

而最终如果我们想要获取到当前bean的实例,还需要从beanDefinitionMap取出对应的BeanDefinition,然后通过getMergedBeanDefinition转换为合并后的RootBeanDefinition,同时会放入mergedBeanDefinitionHolders集合中去


我们最关心的应该是三层缓存的查询方法,即getSingleton:

	public Object getSingleton(String beanName) {
	//第二个参数表示是否允许循环依赖,这里允许
		return getSingleton(beanName, true);
	}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先查询一级缓存--即单例缓存池,如果这里面有就直接返回
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果没有,并且当前bean正在创建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			//查询二级缓存,判断有无
			singletonObject = this.earlySingletonObjects.get(beanName);
			//如果二级缓存也没有,并且允许循环依赖
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
						//查询三级缓存--取出当前bean关联的单例工厂
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							//如果存在的话
							if (singletonFactory != null) {
								//从工厂中获取到这个单例对象
								singletonObject = singletonFactory.getObject();
								//加入二级缓存
								this.earlySingletonObjects.put(beanName, singletonObject);
								//并从三级缓存中移除这个单例工厂
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		//找到就返回,否则返回null
		return singletonObject;
	}

我相信各位此时都有一个疑惑,为什么需要singletonFactories,而不是直接两级缓存完事了呢?

  • 这个问题涉及到aop代理相关,这里先卖个关子,下面我会好好讲讲

先来看看getSingleton方法:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
		//首先查看单例缓存池中是否已经存在了当前bean
		//即当前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!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				//记录当前bean正处于创建状态--加入singletonsCurrentlyInCreation
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
				//这里就是调用createBean方法来创建单例对象
					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;
					}
					//当前单例对象创建完毕后
					//清除其正处于创建状态的标记,即从singletonsCurrentlyInCreation集合中移除
					afterSingletonCreation(beanName);
				}
				//如果是一个新的单例对象被创建
				if (newSingleton) {
				//加入一级缓存中去--顺便清除二三级缓存中当前bean的信息
					addSingleton(beanName, singletonObject);
				}
			}
			//返回创建的单例对象
			return singletonObject;
		}
	}

下面再来看看createBean方法:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
        ...  
		RootBeanDefinition mbdToUse = mbd;
        ... 

		try {
			//初始化前,先去调用bean的后置处理器两个初始化前后的回调接口
			//如果在这里,返回的值不为空,那么会直接形成短路
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
		//真正创建bean
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
....
	}

看来还需要再追踪一层doCreateBean:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
	      ...
		if (instanceWrapper == null) {
		//实例化bean
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		//从BeanWrapper中取出真正被创建出来的bean实例
		Object bean = instanceWrapper.getWrappedInstance();
		...

		//调用相关bean后置处理器的回调接口
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
			....
			}
		}

       //当前bean是否允许早期被暴露出去
       //三个前提: 当前bean是单例的,允许循环依赖,当前bean处于正创建的状态
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		    ...
		    //加入单例工厂---就是加入三级缓存中
		    //这里getEarlyBeanReference是关键,这里会调用bean后置处理器的getEarlyBeanReference接口
		    //自动代理创建器在此处创建代理对象
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		//拿到暴露出去的bean实例
		Object exposedObject = bean;
		try {
		//属性赋值
			populateBean(beanName, mbd, instanceWrapper);
		//初始化bean
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
	          ....
		}

       //当前bean是否允许早期暴露---满足上面三个条件
		if (earlySingletonExposure) {
		//此时第二个参数为false,表示不允许提前暴露
		//因此不会去查询三级缓存---只会去查询到二级缓存
		//当前bean因为还处于创建状态,因此一级缓存是绝对没有的,二级缓存可能有,可能没有
			Object earlySingletonReference = getSingleton(beanName, false);
			//如果二级缓存有,说明产生了循环依赖 
			if (earlySingletonReference != null) {
			   //如果经历过属性赋值和初始化过后的bean依然和之前的bean实例一致
			   //说明当前bean没有被代理过,否则就是两个对象了
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				//如果当前bean被代理了,那么默认是不支持循环引用发生的
				//下面再判断被代理后的对象是否允许在存在循环引用的情况下再注入其他bean实例,默认为false
				//并且当前bean确实存在对其他bean的依赖
				//这里举个例子:
				//例如A与B相互引用,此时A走到这里,说明引用的B已经从三级缓存中获取到了提早暴露出的A的引用,并放入了二级缓存
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				//A这里拿到的应该是B
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						//这里会判断B是否在alreadyCreated集合中,即B是否已经创建好了
						//如果是的话,该方法返回false,下面的条件成立,会将当前依赖bean加入集合,表示存在循环依赖
						//否则,返回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 " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		//处理当前bean相关的销毁回调接口
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		...
		return exposedObject;
	}

从上面代码的分析中,我们洞悉到了一点:

  • 如果在bean初始化过程中,通过相关bean的后置处理器回调接口返回了被代理后的bean对象,并且当前bean对象存在循环引用的情况下,默认会抛出异常,即不支持对被代理后的bean对象的循环引用。

模拟流程

这里我们可以模拟一下Spring三级缓存解决循环依赖的过程:

在这里插入图片描述

  • getBean(A),首先查询三级缓存,因为是第一次创建,因此三级缓存没有,直接返回。
  • 实例化A之后,构造相应的单例工厂,加入三级缓存,进入属性赋值阶段,发现依赖B,去getBean(B)
  • getBean(B),首先查询三级缓存,因为是第一次创建,因此三级缓存没有,直接返回。
  • 实例化B之后,构造相应的单例工厂,加入三级缓存,进入属性赋值阶段,发现依赖A,去getBean(A)
  • getBean(A),首先查询三级缓存,因为A还没初始化完毕,因此还没有被放入一级缓存中去,此时二级缓存也没有,然后发现三级缓存有
  • 从三级缓存中取出对应的单例工厂,然后将提前暴露的A放入二级缓存,并从三级缓存移除对应的单例工厂
  • 直接返回缓存中的A
  • B进行初始化,然后进行循环依赖最后的校验,发现二级缓存中不存在提前暴露的B,说明B没有循环依赖,因此是安全的,直接返回创建好的B实例
  • A获取到返回的B,然后进行初始化,进入最后的循环依赖校验阶段
  • 发现二级缓存中存在提前暴露的A,说明A有被循环依赖
  • 然后校验A是否在初始化过程中被代理,如果被代理了并且存在依赖bean,那么抛出异常
  • 否则正常返回,然后结束

aop与循环依赖

我相信各位还记得getEarlyBeanReference方法,也就是从三级缓存中取出提早暴露的bean时,会调用该方法,那么这与aop有什么关系呢?

getEarlyBeanReference实际会去调用相关后置处理器的回调接口来对bean进行处理,而不是单单返回原本的bean实例:

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
			//调用相关后置处理器的getEarlyBeanReference回调接口
				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
		return exposedObject;
	}

通过追踪getEarlyBeanReference方法的实现子类,我们可以发现只有AbstractAutoProxyCreator实现了此方法,即抽象的自动代理创建器实现了这个方法:

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		//为当前bean构造一个缓存key
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		//加入提前被代理的bean引用集合
		this.earlyProxyReferences.put(cacheKey, bean);
		//判断当前bean是否需要被代理
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

wrapIfNecessary决定当前被提早暴露的bean是否需要被代理,那么判断条件是什么呢?

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//targetSourcedBeans集合中存在,也表明是已经被代理过的
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		//如果advisedBeans集合存在当前bean的缓存信息,并且上一次判断后,说明当前bean不需要被代理
		//那么直接返回
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		//如果是一些基础类或者内部使用的类,则不进行代理
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		//获取可以应用到当前bean的相关拦截器--getAdvicesAndAdvisorsForBean由子类实现
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		//如果返回的拦截器数组为空,说明不需要代理,否则进行代理
		if (specificInterceptors != DO_NOT_PROXY) {
		//创建代理,然后缓存后返回
		//Boolean.TRUE表名当前bean被判断过是需要进行代理的
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}
        //返回原bean,未被代理
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

可以看到getEarlyBeanReference接口可以确保被提前暴露的bean,如果是需要被代理的,那么直接通过这里的回调接口返回代理对象,这样在存在循环依赖的情况下,注入的对象也是代理对象,而非原对象。


postProcessAfterInitialization: bean的初始化方法被调用后执行

	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	//bean==null,说明applyBeanPostProcessorsBeforeInitialization返回的是null
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// remove方法返回被移除的value,上面说了它记录的是原始bean
			// 若被循环引用了,那就是执行了上面的`getEarlyBeanReference`方法,所以此时remove返回值肯定是==bean的(注意此时方法入参的bean还是原始对象)
			// 若没有被循环引用,getEarlyBeanReference()不执行 所以remove方法返回null,所以就进入if执行此处的创建代理对象方法
		//只有在不存在循环引用的条件下,才会进入if语句
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

这里可能有小伙伴会疑问,如果getEarlyBeanReference()被调用后返回的是代理后的对象,那么postProcessAfterInitialization()方法的返回的是原始bean对象,这不是对不上了吗?


  • 要想搞清楚这一点原因,还是要回到上面的doCreateBean方法源码中

先来看看存在循环依赖并且自动代理创建器将提前暴露的bean给代理了的情况:

  • 如果自动代理创建器在getEarlyBeanReference方法返回的是代理对象,那么postProcessAfterInitialization就会返回原对象

在这里插入图片描述
如果只有自动代理创建器的情况下,由于存在循环依赖getEarlyBeanReference方法返回的是代理对象,那么postProcessAfterInitialization就会返回原对象,因此exposedObject和bean是相等的,所以会将提前暴露的bean赋值给exposedObject对象

  • 很多人会在这里犯迷糊: 提早暴露的代理对象和原来的对象压根就不是同一个对象呀,并且这个代理对象也没有经过属性赋值和初始化阶段,直接返回难道没问题吗?

当然没问题,之所以会有这个疑惑,是因为对动态代理底层不够了解,cglib动态代理和jdk动态代理在生产代理对象的时候,是需要获取到目标对象引用的,这个引用和后面被经过属性赋值和初始化阶段的bean引用是同一个,然后再方法被调用的时候,是会去调用内部保存的目标对象方法的,因此最终访问的是同一个对象,没得问题。

  • 如果没有产生循环引用,getEarlyBeanReference方法没被调用,那么postProcessAfterInitialization就会尝试返回代理对象

在这里插入图片描述

代理对象底层的拥有的目标对象和原始bean的引用是一致的,这点要牢记,不然容易搞迷糊

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:21:09  更:2022-06-14 22:22:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:01:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码