spring循环依赖是怎么解决的,为什么要三级缓存而不是二级

本文详细介绍了Spring中bean的创建步骤,包括实例化、依赖注入和初始化,以及在创建过程中如何利用三级缓存解决循环依赖问题。在遇到循环依赖时,Spring会先将半成品bean存入缓存,确保在依赖注入时能正确处理。此外,还探讨了为什么需要三级缓存,特别是对于AOP代理bean的处理。通过实例展示了循环依赖的具体解决过程。
摘要由CSDN通过智能技术生成

在说明循环依赖之前,这里有必要先看一下spring的bean的创建过程。

spring中bean的创建过程

使用AnnotationConfigApplicationContext创建上下文,在refresh()方finishBeanFactoryInitialization方法会将beanDefinitionNames中的bean名称依次创建bean

getBean方法会通过反射的方式new一个bean对应的类(实例化),然后会将依赖的类注入,最后完成初始化,同时这个bean对象会被放入singletonObjects的这个ConcurrentHashMap中,

一个bean的创建就完成了。这个核心方法是在AbstractAutowireCapableBeanFactory的doCreateBean方法中。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
        // 实例话,通过反射创建一个空对象。
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

	    ...
        ...
        ...
        // 将半成品放入缓存,解决循环依赖
		// 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 {
            // 装载bean这里会完成依赖的注入
			populateBean(beanName, mbd, instanceWrapper);
            // *初始化bean,并且在这里判断该bean是否被aop代理,如果被aop代理则创建一个代理对象代替原来的对象。
			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);
			}
		}
           
        ...
        ...
        ...

		return exposedObject;
	}

在instanceWrapper.getWrappedInstance()完成实例化,populateBean()完成依赖注入,initializeBean()完成初始化和aop代理类的生成。

循环依赖问题的产生

在populateBean的过程中,如果依赖的一个bean还未被创建,则会执行getBean方法创建一个bean。假设serviceA依赖serviceB,serviceB又依赖serviceA。serviceA执行populateBean的时候发现serviceB还没有被创建则取创建serviceB,这时serviceB又在populateBean的过程中发现需要创建serviceA,这样就进入了死循环,就发生了循环依赖的问题。那如何解决循环依赖呢,接下来就进入正题。

spring是如何解决循环依赖问题的

每一个bean会经过实例化和依赖注入以及初始化三步,在实例化后依赖注入之前,先将自己刚实例化的bean缓存,即我们常说的半成品的bean。如果有其他bean在创建时发现需要这个bean则会先从缓存中获取到这个半成品先注入,随后半成品bean随着自己依赖注入以及初始化最终完成一个完整的bean。

在上面的doCreateBean源码中,createBeanInstance实例化之后将空对象(半成品)放入缓存中addSingletonFactory()

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);
		}
	}
}

创建bean之前的doGetBean都会先去缓存中查看是否已经创建好了,在getSingleton方法中

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

		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 + "'");
				}
			}
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
        else{
        ...
        ...
        ...
        }
}

重点看getSingleton方法

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;
}

singletonObjects、earlySingletonObjects、singletonFactories就是所谓的三级缓存,其实也是三个ConcurrentHashMap。

singletonObjects是一级缓存,保存创建完成的单例bean。我们重点关注二级缓earlySingletonObjects和三级缓存singletonFactory,一级缓存可以进行忽略。

从代码逻辑中可以看出,先从一级缓存中查找,一级缓存中没有的话再从二级缓存中找,二级缓存中没有的话会去三级缓存singletonFactories中查找,singletonFactories存放的是目标对象的ObjectFactory,也就是说将一个创建工厂放入缓存中。singletonFactory.getObject()最终会调用getEarlyBeanReference()方法,getEarlyBeanReference方法大致是如果这个bean被AOP切面代理则返回一个代理对象,如果没有则返回原bean实例。从三级缓存中获取到bean后会将该bean的三级缓存删除,随后放入二级缓存中。

示例:

创建一个bean依赖一个被aop代理的bean

@Component
public class ZtestComp {

    @Autowired
    private TestService testService;
}
@Component
public class TestService{

    @Autowired
    private ZtestComp ztestComp;

    public void query() {
        System.out.println("testService do query");
    }
}
@Aspect
@Component
public class TestAspect {

    @Pointcut("execution(* ww.com.growup.spring.ioc.circleinjectdemo.*.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void advice(){
        System.out.println("aop before---");
    }
}
@Configuration
@ComponentScan("ww.com.growup.spring.ioc.circleinjectdemo")
@EnableAspectJAutoProxy
public class AnnotationDependencyInjectionResolutionDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(AnnotationDependencyInjectionResolutionDemo.class);
        applicationContext.setAllowCircularReferences(true);
        applicationContext.refresh();
        TestService i = (TestService) applicationContext.getBean("testService");
        i.query();
        applicationContext.close();
    }
}

maven依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.17</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.geronimo.bundles</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.6.8_2</version>
    </dependency>

上图案例中,会先创建testService,实例化后将AbstractAutowriteCapableBeanFactory放入singletonFactory,并传入了testService的BeanDefinition以便后续创建bean提供相应的元数据信息。

在创建testService的过程中发现要注入ztestComp,这时会调用getBean,atestComp的bean还未被创建,就会调用doCreateBean创建atestComp。

 在ztestComp的装载bean的过程中发现需要注入testService,则又会去调用doGetBean获取testService,这时在getSingleton中就会从三级缓存中拿出一个testService的ObjectFactory调用singletonFactory.getObject()方法创建一个testService的代理类。

我们可以看到,singletonFactory.getObject()返回的是一个TestService的CGLB代理类。然后将这个代理类的半成品放入二级缓存中,删除一级缓存,如果有其他的bean依TestService的话就直接从二级缓存中取即可。

ztestComp获取到testService的半成品之后继续做其他的依赖注入和初始化完成ztestComp bean的创建,ztestComp创建完成后,testService中对于ztestComp的依赖注入也告一段落了,testService继续将半成品完成其他的依赖注入和初始化。这样testService和ztestComp的循环依赖也算解决完了。

为什么要三级缓存而不是二级缓存呢?

那么这里有人会问了,那为啥不直接创建aop代理的半实例bean放入二级缓存中,不要三级缓存呢。但是前面的bean的创建过程中可以看出,bean创建aop代理对象是在populateBean装载bean之后的initializeBean方法中创建的,所以如果这个bean没有被aop代理的话二级缓存足矣,但是如果被aop代理的话在刚实例化后时还未能创建aop,所以只好将创建bean的工厂放入等需要创建bean的时候再调用工厂的创建bean的方法。

关于aop的bean创建的流程可以看这篇《spring aop源码解析,带你了解aop流程》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值