文章目录
- 一、简介
- 1. 回顾
- 2. 循环依赖
- 3. Bean的生命周期回顾
- 4. 三级缓存
- 5. 解决循环依赖的思路
- 二、源码分析
- 三、相关问题
- 1. @Async情况下的循环依赖解析
- 2. 原型Bean情况下的循环依赖解析
- 3. 构造方法导致的循环依赖解析
一、简介
1. 回顾
前面首先重点分析了Spring Bean的整个生命周期的源码,同时还分析了Spring底层依赖注入的原理,依赖注入中的一个关键问题,即循环依赖问题在本部分详细分析
2. 循环依赖
首先什么是循环依赖?很简单,就是A对象依赖了B对象,B对象依赖了A对象。
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。但是,在Spring中循环依赖就是一个问题了,为什么? 因为,在Spring中,一个对象并不是简单new出来了,而是会经过一系列的Bean的生命周期,就是因为Bean的生命周期所以才会出现循环依赖问题。当然,在Spring中,出现循环依赖的场景很多,有的场景Spring自动帮我们解决了,而有的场景则需要程序员来解决,下文详细来说。
3. Bean的生命周期回顾
这里不会对Bean的生命周期进行详细的描述,只描述一下大概的过程。
- Spring扫描class得到BeanDefinition
- 根据得到的BeanDefinition去生成bean
- 首先根据class推断构造方法
- 根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象)
- 填充原始对象中的属性(依赖注入)
- 如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象
- 把最终生成的代理对象放入单例池(源码中叫做singletonObjects)中,下次getBean时就直接从单例池拿即可
比如上文说的A类,A类中存在一个B类的b属性,所以,当A类生成了一个原始对象之后,就会去给b 属性去赋值,此时就会根据b属性的类型和属性名去BeanFactory中去获取B类所对应的单例bean。 如果此时BeanFactory中存在B对应的Bean,那么直接拿来赋值给b属性;如果此时BeanFactory中 不存在B对应的Bean,则需要生成一个B对应的Bean,然后赋值给b属性。问题就出现在第二种情况,如果此时B类在BeanFactory中还没有生成对应的Bean,那么就需要去生成,就会经过B的Bean的生命周期。那么在创建B类的Bean的过程中,如果B类中存在一个A类的a属性,那么在创建B的Bean的过程中就 需要A类对应的Bean,但是,触发B类Bean的创建的条件是A类Bean在创建过程中的依赖注入,所以 这里就出现了循环依赖:
ABean创建–>依赖了B属性–>触发BBean创建—>B依赖了A属性—>需要ABean(但ABean还在创建过程中)
从而导致ABean创建不出来,BBean也创建不出来。这是循环依赖的场景,但是上文说了,在Spring中,通过某些机制帮开发者解决了部分循环依赖的问 题,这个机制就是三级缓存。
4. 三级缓存
三级缓存是通用的叫法。 一级缓存为:singletonObjects 二级缓存为:earlySingletonObjects 三级缓存为 **:singletonFactories**,先稍微解释一下这三个缓存的作用,后面详细分析:
- singletonObjects中缓存的是已经经历了完整生命周期的bean对象。
- earlySingletonObjects比singletonObjects多了一个early,表示缓存的是早期的bean对象。 早期是什么意思?表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。
- ingletonFactories中缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的 工厂。
5. 解决循环依赖的思路
A的Bean在创建过程中,在进行依赖注入之前,先把A的原始Bean放入缓存(提早暴露,只要放到缓 存了,其他Bean需要时就可以从缓存中拿了),放入缓存后,再进行依赖注入,此时A的Bean依赖 了B的Bean,如果B的Bean不存在,则需要创建B的Bean,而创建B的Bean的过程和A一样,也是先创建一个B的原始对象,然后把B的原始对象提早暴露出来放入缓存中,然后在对B的原始对象进行依赖注入A,此时能从缓存中拿到A的原始对象(虽然是A的原始对象,还不是最终的Bean),B的原始对象依赖注入完了之后,B的生命周期结束,那么A的生命周期也能结束。因为整个过程中,都只有一个A原始对象,所以对于B而言,就算在属性注入时,注入的是A原始 象,也没有关系,因为A原始对象在后续的生命周期中在堆中没有发生变化。从上面这个分析过程中可以得出,只需要一个缓存就能解决循环依赖了,那么为什么Spring中还需要 singletonFactories呢?
这是难点,基于上面的场景想一个问题:如果A的原始对象注入给B的属性之后,A的原始对象进行了 AOP产生了一个代理对象,此时就会出现,对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。
AOP就是通过一个BeanPostProcessor来实现的,这个BeanPostProcessor就是 AnnotationAwareAspectJAutoProxyCreator,它的父类是AbstractAutoProxyCreator,而在 Spring中AOP利用的要么是JDK动态代理,要么CGLib的动态代理,所以如果给一个类中的某个方法 设置了切面,那么这个类最终就需要生成一个代理对象。
一般过程就是:A类—>生成一个普通对象–>属性注入–>基于切面生成一个代理对象–>把代理对 象放入singletonObjects单例池中。
而AOP可以说是Spring中除开IOC的另外一大功能,而循环依赖又是属于IOC范畴的,所以这两大功能想要并存,Spring需要特殊处理。如何处理的,就是利用了第三级缓存singletonFactories。
二、源码分析
首先我们回到doCreateBean源码:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 实例化bean
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 有可能在本Bean创建之前,就有其他Bean把当前Bean给创建出来了(比如依赖注入过程中)
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建Bean实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 后置处理合并后的BeanDefinition
// Allow post-processors to modify the merged bean definition.
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;
}
}
// 为了解决循环依赖提前缓存单例创建工厂
// 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));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 循环依赖-添加到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性填充
populateBean(beanName, mbd, instanceWrapper);
// 初始化
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) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// beanName被哪些bean依赖了,现在发现beanName所对应的bean对象发生了改变,那么则会报错
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
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是否是单例&&当前spring是否支持循环依赖&&当前的bean是否正在创建
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 循环依赖-添加到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
上面代码在做了一系列判断后,将一个lamda表达式添加到第3级缓存中。虽然添加到了三级缓存中,但我们需要知道spring此时是不知道当前bean是否出现循环依赖的,此时它仅仅是没有目的存。
第三级缓存就是ingletonFactories
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
//这部分是线程安全的
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
上面代码就完成了第三级缓存的填充,只有在属性真正注入的时候我们才需要开始着手去处理循环依赖问题。在属性注入的时候,我们需要通过getbean方法来获取我们需要的bean,我们进入getbean方法。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
然后getBean调用doGetBean
方法。
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// name有可能是 &xxx 或者 xxx,如果name是&xxx,那么beanName就是xxx
// name有可能传入进来的是别名,那么beanName就是id
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 如果sharedInstance是FactoryBean,那么就调用getObject()返回对象
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// &&&&xxx---->&xxx
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 检查BeanDefinition是不是Abstract的
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// dependsOn表示当前beanName所依赖的,当前Bean创建之前dependsOn所依赖的Bean必须已经创建好了
for (String dep : dependsOn) {
// beanName是不是被dep依赖了,如果是则出现了循环依赖
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// dep被beanName依赖了,存入dependentBeanMap中,dep为key,beanName为value
registerDependentBean(dep, beanName);
// 创建所依赖的bean
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try { // session.getAttriute(beaName) setAttri
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
}
}
}
catch (BeansException ex) {
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
finally {
beanCreation.end();
}
}
// 检查通过name所获得到的beanInstance的类型是否是requiredType
return adaptBeanInstance(name, beanInstance, requiredType);
}
Object sharedInstance = getSingleton(beanName);
尝试从一级缓存singletonObjects
,获取所要注入的bean对象
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
//如果没有从单例
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
如果我们顺利从单例池中找到了我们需要的bean,就可以直接返回完成属性注入了。如果没有找到,它会调用isSingletonCurrentlyInCreation(beanName)
判断当前的bean是否正在创建中
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
singletonsCurrentlyInCreation
是一个集合,用来存储当前正在创建的bean。
由于bean的创建在一个线程中是串行的,所以我们依赖的那个bean可能还没创建,所以会返回false。所以上面函数直接返回空,回到doGetBean
方法。现在就需要创建这个被依赖的bean了。而创建这bean的时候,依赖注入的时候,发现这个bean(这里假设为Bservice)也有依赖的对象,同样要进行上面的逻辑,进入doGetBean
方法,查找这个依赖所依赖的bean。if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))
,此时这个if判断就可以成功进入,因为依赖当前bean的之前的那个bean(这里假设为Aservice)也在创建中。然后执行if后面的代码。首先
singletonObject = this.earlySingletonObjects.get(beanName);
上面代码从二级缓存this.earlySingletonObjects
中拿,此时是拿不到的,根据前面的分析我们只是会在创建AService时将有关Aservice的一个lamda表达式没有目的的存在了三级缓存中(所有的bean在创建的时候都会有这个动作)。所以上面代码会返回一个空。
if (singletonObject == null && allowEarlyReference) {
如果拿到了空,就判断你是否支持循环依赖,默认是支持的,所以if判断成功继续执行后面的代码(此时说明出现了循环依赖,这里就判断了循环依赖)。
synchronized (this.singletonObjects) {
//此时有可能创建好了,再次从一级缓存(单例池中拿)
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//如果一级缓存中没有拿到,再从二级缓存中拿
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//二级缓存中没有拿到,从三级缓存中拿
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//执行lamda表达式
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
由于我们这个是循环依赖场景,所以一定会执行这句ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
代码,从三级缓存中拿。这里就拿到了前面的lamda表达式。singletonObject = singletonFactory.getObject();
这句就执行了lamda表达式。生成了一个半成品的AService
,然后将其放入二级缓存汇总,同时从三级缓存中移除当前的lamda表达式。上面就是解决循环依赖的关键代码。那么lamda表达式到底做了什么事,我们回到前面的代码中。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
发现lamda表达式中执行了一个getEarlyBeanReference
方法。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
//遍历beanpostprocess
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
Object exposedObject = bean;
就是我们需要提前暴露的bean。然后执行 bp.getEarlyBeanReference(exposedObject, beanName);
。
//循环依赖提前进行AOP的方法
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
//初始化后正常进行AOP的方法
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这个方法是AbstractAutoProxyCreator类中的,该类是AOP的关键类。而getEarlyBeanReference
就是用来提前进行AOP的方法。this.earlyProxyReferences.put(cacheKey, bean);
这个map用来记录当前这个bean提前进行了AOP,wrapIfNecessary(bean, beanName, cacheKey);
该方法用来判断当前bean是否需要进行AOP,如果需要就进行AOP。这部分后面分析AOP的时候会详细进行分析。
为什么要提前进行AOP,我们知道前面我们是将一个Lamda表达式存入了三级缓存中,lamda表达式需要调用一个
getEarlyBeanReference(beanName, mbd, bean)
方法,bean参数就是当前正在创建的Bean的一个半成品,通过遍历BeanPostProcessors来判断当前类是不是有AOP,如果没有我们会直接返回一个当前bean得半成品对象,然后后面会将这个bean对象存入二级缓存中,用于依赖注入。如果有AOP,它这个lamda表达式就会提前进行AOP,返回一个当前正在创建Bean的AOP对象,那么这种机制就解决了一个关键问题,因为AOP通常是在初始化之后,由于循环依赖的存在,我们需要将未创建好的对象提前进行注入,如果我们注入的是bean原始的半成品对象,而其实我们需要的是一个AOP对象,那么当前bean创建完成后就会发现注入的对象不一致的问题—即注入的不是最终的AOP代理对象。所以这里要提前进行AOP,在Bservice创建的过程中,提前对AService进行AOP生成其代理对象,然后注入到BService属性中完成BService的创建,最后AService注入已经创建好的BService对象,这样就成功解决了循环依赖问题。
当循环依赖的背景下,我们提前进行了AOP,到初始化后,当我们需要真正进行AOP的时候,就会调用postProcessAfterInitialization
方法。
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
该方法首先判断this.earlyProxyReferences.remove(cacheKey) != bean
,判断我们是否已经进行了AOP,如果已经进行了AOP就会直接返回原始对象,否则开始真正的AOP。
this.earlyProxyReferences这个map就存储了提前进行了AOP的代理对象
继续回到doCreateBean
方法。前面通过 exposedObject = initializeBean(beanName, exposedObject, mbd);
进行了bean的初始化的动作,根据postProcessAfterInitialization
我们知道,如果我们没有进行提前AOP的话,它会执行真正的AOP返回一个代理对象,如果进行了提前AOP,它会返回一个原始bean,但我们知道我们压根就不需要原始的bean,那Spring是怎么对这个原始bean进行处理,最后向单例池中存入我们需要的AOP的代理对象的,接着看后面代码
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// beanName被哪些bean依赖了,现在发现beanName所对应的bean对象发生了改变,那么则会报错
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
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.");
}
}
}
}
首先调用Object earlySingletonReference = getSingleton(beanName, false);
,再次从单例池中拿对象。我们回到getSingleton
方法的逻辑,首先它会从一级缓存中拿对象,现在肯定是拿不到的,然后从二级缓存中拿,现在是肯定拿的到,而且拿到的是通过前面lamda表达式生成的AOP提前代理对象(AService的提前AOP对象)。
然后我们接着执行
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
它会对我们从二级缓存中拿到的对象与前面真正AOP的过程中返回的对象进行比较,我们这里这种情况,exposedObject
返回的是一个原始的bean,而bean
代表是原始bean。所以这里肯定是相等的。然后将exposedObject
设置为二级缓存中的提前AOP代理对象。最后doCreateBean
就会返回这个提前AOP对象。到此AService就创建完了
,然后注入到BService的属性中,BService也可以成功创建完,最后AService将创建好的BService也注入到自己的属性中,这样循环依赖也解决了。下图总结了大致流程:
三、相关问题
1. @Async情况下的循环依赖解析
在Spring中,使用 @Async 注解时,可能会遇到循环依赖的问题。@Async 注解导致问题的底层原因涉及到Spring框架中对异步方法的处理方式。当使用 @Async 注解时,Spring会在运行时创建一个代理,该代理负责异步执行被注解的方法。这涉及到创建一个新的线程或使用线程池来执行方法,并且可能导致异步方法的执行时机不同于同步方法。下面用案例演示一下这个情况:
- 创建AService
@Component
public class AService {
@Autowired
BService bService;
@Async
public void asyncMethod() {
// 在异步方法中使用myOtherService...
bService.doSomething();
}
}
AService中的asyncMethod方法上面加了@Async注解,spring底层后创建一个AService的代理对象,然后实现异步调用
- 创建BService
@Component
public class BService {
@Autowired
AService bService;
public void doSomething() {
System.out.println("BService");
}
}
- 给两个Service加入AOP
@Aspect
@Component
public class MyAspect {
@Before("execution(* com.zhouyu.service.AService.*(..))")
public void beforeAServiceMethodExecution() {
System.out.println("Before executing a method in AService.");
}
@Before("execution(* com.zhouyu.service.BService.*(..))")
public void beforeBServiceMethodExecution() {
System.out.println("Before executing a method in BService.");
}
}
- 配置类开启AOP支持
@ComponentScan("com.zhouyu")
@EnableScheduling
@PropertySource("classpath:spring.properties")
@EnableAspectJAutoProxy
public class AppConfig {
}
- 编写测试类
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取 BoxService bean
AService aService = (AService) applicationContext.getBean("AService");
// 关闭 ApplicationContext
applicationContext.close();
}
}
发现报错类,为什么会报错我们分析一下。其实当我们加入@Async注解后,在AOP之后会在原来的7个BeanPostProcessor的基础上多一个AsynAnnotationBeanPostProcessor,而且它在我们的AOP之后(AnnotationAwareAspectJAutoProxy这个生成AOP代理对象的BeanPostProcessor之后),这个会生成一个新的代理对象(不是AOP在循环依赖情况下返回的原始bean,而是生成一个新的代理对象),用于异步执行被@Async注释的方法。
然后这就会改变我们我们原来生成的AOP对象,这样当我们执行下面代码执行if (exposedObject == bean) {
这个exposedObject和bean不一样,而执行else if后面的代码,然后抛出异常。
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// beanName被哪些bean依赖了,现在发现beanName所对应的bean对象发生了改变,那么则会报错
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
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.");
}
}
}
}
那么怎么解决这个问题,思路是加上@Lazy
注解
然后就不报错了。但为什么lazy会解决这个问题,我们知道Lazy是延迟加载,在创建AService由于需要BService对象进行注入,当AService来创建BService时看到它有Lazy注解,所以会直接返回BService的一个代理对象注入AService,这样就不会有下面的流程,所以就解决了上面的问题(因为循环依赖压根就没有产生)。
2. 原型Bean情况下的循环依赖解析
我们给AService和BService都变成原型Bean
@Component
@Scope("prototype")
public class AService {
@Autowired
BService bService;
public void asyncMethod() {
// 在异步方法中使用myOtherService...
bService.doSomething();
}
}
@Component
@Scope("prototype")
public class BService {
@Autowired
AService bService;
public void doSomething() {
System.out.println("BService");
}
}
那么这种方式可以解决循环依赖问题吗,答案是不能,下面我们分析一下。首先需要创建AService,创建AService的时候发现需要BService进行注入,那么就开始创建BService,而创建BService的时候又需要创建AService,而AService是一个原型bean,所以会创建一个新的AService,所以我们用不了三级缓存中创建好的临时对象,然后AService又创建BService此时就不断重复上面过程,解决上面问题的方法就是将其中一个Bean设置为单例的就行。
3. 构造方法导致的循环依赖解析
其实构造方法导致的循环依赖的情况和使用Autowire是一样的
@Component
public class AService {
BService bService;
public AService(BService bservice){
this.bService=bservice;
}
public void asyncMethod() {
// 在异步方法中使用myOtherService...
bService.doSomething();
}
}
@Component
public class BService {
AService aService;
public BService(AService aservice){
this.aService=aservice;
}
public void doSomething() {
System.out.println("BService");
}
}
在创建AService过程中,由于只有一个构造函数,Spring就会调用这个构造函数来创建AService,而发现构造函数的参数需要一个BService,所以去创建一个BService,而BService创建又需要AService,此时会直接报错,这点和@Autowired不同,因为@Autowired底层实际通过调用默认构造函数已经创建了一个临时对象,而构造函数这种类型的循环依赖由于构造函数都没有执行完,所以这个临时对象都没有生成,所以直接报错,解决方法同样是Lazy注解。