Spring之循环依赖底层源码(一)

文章目录

    • 一、简介
      • 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会在运行时创建一个代理,该代理负责异步执行被注解的方法。这涉及到创建一个新的线程或使用线程池来执行方法,并且可能导致异步方法的执行时机不同于同步方法。下面用案例演示一下这个情况:

  1. 创建AService
@Component
public class AService {
	@Autowired
	BService bService;

	@Async
	public void asyncMethod() {
		// 在异步方法中使用myOtherService...
		bService.doSomething();
	}
}

AService中的asyncMethod方法上面加了@Async注解,spring底层后创建一个AService的代理对象,然后实现异步调用

  1. 创建BService
@Component
public class BService {
	@Autowired
	AService bService;

	public void doSomething() {
		System.out.println("BService");
	}
}
  1. 给两个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.");
	}
}
  1. 配置类开启AOP支持
@ComponentScan("com.zhouyu")
@EnableScheduling
@PropertySource("classpath:spring.properties")
@EnableAspectJAutoProxy
public class AppConfig {


}
  1. 编写测试类

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注解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用 CMake 生成一个静态库

文章目录 tutorial_3/CMakeLists.txttutorial_3/src/CMakeLists.txtcmake_tutorial/tutorial_3/src/hello.cpptutorial_3/src/hello.h根目录的 CMakeLists.txtsrc 目录的 CMakeLists.txthello.cpp 和 hello.h构建过程总结 tutorial_3/CMakeLists.txt cmake_minimum_required(V…

excel3基础图标与透视图表的制作

业务需求1 给逾期金额也加上条形图,同时制作各个城市的逾期发展趋势 思路&#xff1a; 操作顺序&#xff1a;选中3列数据&#xff08;8月上旬逾期金额、8月中旬逾期金额和8月下旬逾期金额&#xff09;–开始–条件格式–数据条–渐变填充&#xff08;第5个&#xff09;&#xf…

厦门大学OpenHarmony技术俱乐部开创“1+N”新模式,加速推动产学研融合

12月29日,OpenHarmony技术俱乐部再添重将——在多方见证下,厦门大学OpenHarmony技术俱乐部在翔安校区益海嘉里楼报告厅正式揭牌成立,现场出席领导及师生代表近千人。 成立仪式现场 OpenHarmony技术俱乐部 携手厦门大学共绘开源生态新图景 OpenHarmony是由开放原子开源基金…

STM32F407ZGT6时钟源配置

1、26M外部时钟源 1、25M外部时钟源

cmd命令启动C# windows服务程序

因为Process.Manage.Service.exe程序为Windows服务程序&#xff0c;不能直接双击打开&#xff0c;所以需要借助windows系统自带InstallUtil.exe程序来启动它。 以管理员身份运行cmd命令控制台窗口 输入命令进入到InstallUtil.exe程序所在的文件夹 cd C:\Windows\Microsoft.NET…

[SAP ABAP] ABAP基础语法

ABAP(Advanced Business Application Programming)是一种编程语言&#xff0c;用于SAP软件的开发和定制 SAP ABAP是一种高级语言&#xff0c;主要用于在SAP平台上为大型企业和金融机构开发企业应用程序 1. ABAP基本语法结构 ABAP源程序由注释和ABAP语句组成&#xff0c;其中…

Linux内核组成

Linux内核的组成 /boot/vmlinuz-4.18.0-80.el8.x86_64 &#xff1a;启动时用到的内核 /lib/modules/4.18.0-80.el8.x86_64 &#xff1a;内核模块 /boot/initramfs-4.18.0-80.el8.x86_64.img &#xff1a;启动时提供必要的内核模块 kernel-core安装包里面包含了Linux内核启动…

Linux驱动学习—内核定时器

1、内核定时器 1.1 Linux内核定时器概念 不同于单片机定时器&#xff0c;LInux内核定时器是一种基于未来时间点的计时方式&#xff0c;以当前时刻来启动的时间点&#xff0c;以未来的某一时刻为终止点。比如&#xff0c;现在是10点5分&#xff0c;我要定时5分钟&#xff0c;那…

Java反射和动态代理

文章目录 1、反射1.1 反射的概述1.2 反射作用1.3 获取字节码文件对象的方式1.4 字节码文件和字节码文件对象1.5 获取构造方法1.6 获取构造方法并创建对象1.7 获取成员变量并获取值和修改值1.8 获取成员方法1.9 获取成员方法并运行1.10 反射练习1.10.1 泛型擦除1.10.2 修改字符串…

vue+nodejs微信小程序基于uniapp的学生宿舍打卡失物招领管理系统

基于微信的宿舍管理系统的设计基于现有的手机&#xff0c;可以实现等功能。方便用户对宿舍管理系统查看个人中心、失物招领管理、失物认领管理、晚归打卡管理、宿舍信息管理、宿舍更新管理、交流论坛、系统管理等功能模块的管理及详细的设计与统计分析。根据系统功能需求建立的…

网络安全法解读之思维导图

一、出台背景 二、法律基础 三、网络安全法架构 1、第一章 总则&#xff08;1-14条&#xff09; 2、第二章 网络安全支持与促进&#xff08;15-20条&#xff09; 3、 第三章 网络运行安全&#xff08;21-39条&#xff09; &#xff08;1&#xff09;第一节 一般规定 &#xf…

Linux无法启动:Timed out waiting for device dev-disk-by

由于CPU风扇问题&#xff0c;导致主机无法启动&#xff1b;鉴于机器本身比较老旧&#xff0c;因此&#xff0c;决定直接把硬盘拆下后更换到新的主机上&#xff0c;更安全可靠&#xff1b;问题却因此而起&#xff1a;把硬盘更换到新主机后&#xff0c;居然无法启动&#xff0c;开…

简易机器学习笔记(九)LeNet实例 - 在眼疾识别数据集iChallenge-PM上的应用

前言 上一节大概讲了一下LeNet的内容&#xff0c;这一章就直接来用&#xff0c;实际上用一下LeNet来进行训练和分类试试。 调用的数据集&#xff1a; https://aistudio.baidu.com/datasetdetail/19065 说明&#xff1a; 如今近视已经成为困扰人们健康的一项全球性负担&…

Windows下默认关闭数字键盘

进入注册表&#xff0c;找到值HKEY_USERS 》 .DEFAULT 》 Control Panel 》 Keyboard &#xff0c;点击 Keyboard 之后在右侧窗口中找到 InitialKeyboardIndicators&#xff0c;设置为0&#xff0c;保存&#xff0c;重启电脑 该值的意义

深信服技术认证“SCSA-S”划重点:文件包含漏洞

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…

永磁同步电机的磁场定向控制

目录 概述 通过系统仿真验证行为 探索模型架构 生成用于集成到嵌入式应用程序的控制器 C 代码 指定控制器模型的参考行为 创建 PIL 实现 准备用于 PIL 测试的控制器模型 测试生成的代码的行为和执行时间 结论 此示例说明从电机控制算法生成 C 代码并验证其编译行为和执…

分布式事务完美解决方案:消息中间件(kafka)+ 本地事物 + 消息校对

前言 分布式事务是要保证多个服务下的多个数据库操作的一致性。分布式事务常见解决方案有&#xff1a;二阶段、三阶段和TCC实现强一致性事务&#xff0c;其实还有一种广为人知的方案就是利用消息队列来实现分布式事务&#xff0c;保证数据的最终一致性&#xff0c;也就是我们常…

带大家做一个,易上手的家常香干炒腊肉

从冰箱那一块腊肉 套个食品级的袋子 然后用冷水化冰 准备两块香干 香干切成片 不要太薄 当然也别厚了 一把青蒜 青蒜切成段 干和叶子分开装 腊肉去掉下面的肉皮 然后切小块 锅中加入清水 下入少量油和盐 开小火 水起泡泡后下入香干 过水 半分钟左右 香干捞出备用 将腊…

Geoserver扩展发布MySQL视图功能

Geoserver中并不自带mysql数据发布功能&#xff0c;需要扩展外部插件。 1、示例以geoserver-2.20.5版本进行演示&#xff0c;所以MySQL插件需要到该版本对应的“Extensions”标题下查找&#xff0c;下载地址&#xff1a;GeoServer&#xff0c;详见下图 2、选择MySQL进入下载页…

【北邮国院大四上】Business Technology Strategy 企业技术战略

北邮国院电商大四在读&#xff0c;本笔记仅为PPT内容的整理与翻译&#xff0c;并不代表本课程的考纲及重点&#xff0c;仅为本人复习时方便阅读与思考之作。 写在前面 大家好&#xff0c;欢迎来到大学期间的最后一门课程&#xff0c;本门课程是中方课&#xff0c;所以很庆幸的…