`

spring疑难解惑-循环依赖的解决

阅读更多
AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
		Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				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));
//此处表示是否抛出早期引用,以便循环引用时使用,放在三级缓存的singtonFactories里面
//存在循环引用是采用调用ObjectFactory.getObject
		if (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				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) {
//此处从二级缓存的earlySingtonObjects中获取,如何不为空表示,存在循环引用
//在doGetBean方法中,会将三级缓存的singtonFactories调用ObjectFactory.getObject放入到二级缓存的earlySingtonObjects中
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
//如果相等表示,早期引用的bean(即设置到其他bean中的bean引用即为此处的早期引用)
//因为exposedObject = earlySingletonReference;最终都是一个引用
//事务代理属于此类循环引用的实例,事务代理时在AbstractAutoProxyCreater的早期引用中创建的
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
//如果能够容忍创建的bean有多个版本,则可以设置allowRawInjectionDespiteWrapping=true,但这样就破换了spring的单例原则,不建议使用
//hasDependentBean此处表示是否存在引用创建中的bean的其他bean(姑且是beanOther),如果存在则beanOther引用了bean的早期引用,与最终生成的bean是不一致的,肯定不对
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
//此处的TypeCheckOnly=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 " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}



DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 该singletonFactory不为空,在doCreateBean阶段已经放入了ObjectFactory
//此处调用getObject方法生成早期引用,并放入二级环境earlySingletonObjects
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return (singletonObject != NULL_OBJECT ? singletonObject : null);
	}





AbstractAutoProxyCreator#
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
//此处this.earlyProxyReferences中放入参与早期引用的缓存,在postProcessAfterInitialization中就不用再次创建了,保证了doCreateBean中exposedObject == bean
//他们生成的代理功能本质上是一样的都是getAdvicesAndAdvisorsForBean获取的切面
//但仅限于AbstractAutoProxyCreator的子类生成的代理是可以循环引用的,例如事务属于此类
//其他类型的代理或自定义的代理可能没有实现earlyProxyReferences类似功能,导致exposed!=bean
		if (!this.earlyProxyReferences.containsKey(cacheKey)) {
			this.earlyProxyReferences.put(cacheKey, Boolean.TRUE);
		}
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.containsKey(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}


1.为什么要用三级缓存而不用二级缓存
一个设计良好的系统应该不会经常出现循环引用的情况的,在一开始就设置早期引用浪费系统资源;

2.为什么三级缓存中放入ObjectFactory,而不是bean
为了扩展,生成事务代理或其他代理,以便是代理也可以循环引用


分享到:
评论

相关推荐

    spring-ai-core-0.8.1

    spring-ai-core 0.8.1,解决大家使用2023.0.1.0 版本 Spring Cloud Alibaba 依赖,代码依赖下载报错问题, &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt; &lt;artifactId&gt;spring-cloud-alibaba-dependencies &lt;version&gt;...

    spring-context-support-1.0.10-API文档-中文版.zip

    赠送Maven依赖信息文件:spring-context-support-1.0.10.pom; 包含翻译后的API文档:spring-context-support-1.0.10-javadoc-API文档-中文(简体)版.zip; Maven坐标:...

    spring-context-support-5.0.5.RELEASE-API文档-中文版.zip

    赠送Maven依赖信息文件:spring-context-support-5.0.5.RELEASE.pom; 包含翻译后的API文档:spring-context-support-5.0.5.RELEASE-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework:spring-...

    spring-data-commons-2.5.5-API文档-中文版.zip

    赠送Maven依赖信息文件:spring-data-commons-2.5.5.pom; 包含翻译后的API文档:spring-data-commons-2.5.5-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework.data:spring-data-commons:2.5.5;...

    jasypt-spring-boot-starter 3.0.5依赖的pom及jar

    《深入解析jasypt-spring-boot-starter 3.0.5依赖的POM与JAR》 在Java开发领域,构建和管理依赖是至关重要的环节。jasypt-spring-boot-starter是一个流行的安全库,它允许开发者在Spring Boot应用中轻松地实现加密...

    spring-session-redis/spring整合redis管理session依赖jar包

    commons-pool2-2.3.jar,jedis-2.8.0.jar,spring-data-redis-1.6.0.RELEASE.jar,spring-session-1.1.1.RELEASE.jar,Spring-data-redis(Version 1.6.0.RC1)中文版.pdf

    spring-cglib-repack-3.2.0.jar和spring-objenesis-repack-2.1.jar

    解决这个问题的方法通常是将`spring-cglib-repack-3.2.0.jar`和`spring-objenesis-repack-2.1.jar`添加到你的项目构建路径中,如果是Maven或Gradle项目,可以在pom.xml或build.gradle文件中声明对应的依赖。...

    spring-messaging-5.0.8.RELEASE-API文档-中文版.zip

    赠送Maven依赖信息文件:spring-messaging-5.0.8.RELEASE.pom; 包含翻译后的API文档:spring-messaging-5.0.8.RELEASE-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework:spring-messaging:5.0.8...

    解析spring-boot-starter-parent简介

    通过继承spring-boot-dependencies,spring-boot-starter-parent可以提供一系列的依赖项管理功能,包括依赖项版本管理、插件配置等。 spring-boot-starter-parent的主要特点是它可以帮助开发者快速构建Spring Boot...

    spring-data-commons-2.6.1-API文档-中文版.zip

    赠送Maven依赖信息文件:spring-data-commons-2.6.1.pom; 包含翻译后的API文档:spring-data-commons-2.6.1-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework.data:spring-data-commons:2.6.1;...

    spring-boot-starter-parent-1.5.13.RELEASE.zip

    标题 "spring-boot-starter-parent-1.5.13.RELEASE.zip" 提供的信息表明,这是一个与Spring Boot相关的压缩包,具体来说是Spring Boot的起步依赖(Starter Parent)的一个版本,版本号为1.5.13.RELEASE。Spring Boot...

    spring-security-config-5.5.2-API文档-中文版.zip

    赠送Maven依赖信息文件:spring-security-config-5.5.2.pom; 包含翻译后的API文档:spring-security-config-5.5.2-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework.security:spring-security-...

    spring-context-5.3.12-API文档-中英对照版.zip

    赠送Maven依赖信息文件:spring-context-5.3.12.pom; 包含翻译后的API文档:spring-context-5.3.12-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.springframework:spring-context:5.3.12; 标签:...

    spring-cglib-repack-3.2.5.jar,spring-objenesis-repack-2.6.jar

    标题中的"spring-cglib-repack-3.2.5.jar"和"spring-objenesis-repack-2.6.jar"是Spring框架在运行时依赖的两个关键库,它们主要涉及到动态代理和对象创建的优化。 1. **CGLIB(Code Generation Library)**: ...

    activiti-spring-boot-starter-basic-6.0.0适配springboot2.1.2

    activiti-spring-boot-starter-basic-6.0.0适配springboot2.1.2

    spring-ai-core 0.8.1

    这意味着你可以直接利用这些框架的模型,并通过Spring的依赖注入机制将它们注入到你的服务中。这种跨框架的兼容性使得项目可以灵活地选择最适合的AI工具,同时保持代码的整洁和可维护性。 在API设计上,Spring AI ...

    spring-session+spring依赖jar包

    spring-session+spring依赖jar包,包含spring4.0.2.RELEASE相关jar包和commons-pool2-2.4.2.jar,jedis-2.7.3.jar,spring-data-redis-1.6.2.RELEASE.jar,spring-session-1.1.1.RELEASE.jar

    spring-web-5.3.10-API文档-中文版.zip

    赠送Maven依赖信息文件:spring-web-5.3.10.pom; 包含翻译后的API文档:spring-web-5.3.10-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework:spring-web:5.3.10; 标签:springframework、...

    spring-oauth-client依赖于spring-oauth-server或MyOIDC, 是OAuth2客户端演示项目

    spring-oauth-client依赖于spring-oauth-server或MyOIDC, 是OAuth2客户端演示项目。

    spring-security-oauth2-2.0.3.jar(包括jar包,源码,doc)

    1. **spring-security-oauth2-2.0.3.RELEASE-javadoc.jar**:这是文档JAR文件,包含Spring Security OAuth2的API文档。通过查看这些文档,开发者可以了解各个类、接口和方法的用途,以及如何在自己的项目中使用它们...

Global site tag (gtag.js) - Google Analytics