Spring 一级 二级 三级缓存

本文详细解释了SpringIoC容器如何通过三级缓存机制解决循环依赖问题,涉及单例对象缓存、早期对象引用以及BeanFactoryAware接口的作用。讨论了如何在Bean的生命周期中处理循环依赖,包括`@Resource`和`@Autowired`的区别以及如何使用代理对象来确保正确实例化。
摘要由CSDN通过智能技术生成

转载:

浅谈一下SpringIOC解决循环依赖_ioc循环依赖-CSDN博客

循环以来解决办法主要流程图

三级缓存源码

类:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

/** Cache of singleton objects: bean name to bean instance. */
一级缓存:完整的初始化完成并且设置完属性的对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
三级缓存:ObjectFactory代理对象
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
二级缓存,初始化完成,但是没有设置完属性的对象
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

保存所有单例bean的名字
	/** Set of registered singletons, containing the bean names in registration order. */
	private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

根据源码从创建并初始化对象开始分析

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

热切地缓存singleton,以便即使在由BeanFactoryAware等生命周期接口触发时也能够解析循环引用。		
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.

// 如果默认的作用域是单例 && 允许循环以来 && 当前的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");
			}
            //getEarlyBeanReference获取早期对象,早期——也就是说,在目标bean实例完全初始化之前。
            //传入一个lambd表达式
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

	/**
        获取对指定bean的早期访问的引用,通常用于解析循环引用。
	 * Obtain a reference for early access to the specified bean,
	 * typically for the purpose of resolving a circular reference.
	 * @param beanName the name of the bean (for error handling purposes)
	 * @param mbd the merged bean definition for the bean
	 * @param bean the raw bean instance
	 * @return the object to expose as bean reference 返回 暴露出当前对象的引用
	 */
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
        //不是合成的类 && 此工厂是否持有InstantiationAwareBeanPostProcessor
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            //遍历bean的后置处理器
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
                //如果是SmartInstantiationAwareBeanPostProcessor类的对象,并且返回对应的后置处理器的对象
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

这里说明首先要将该对象加入到三级缓存中。

    //beanName,lambd表达式
	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
            //如果一级缓存不包含当前的对象,那么就将当前对象add到三级缓存中singletonFactories
            //也要清空一下earlySingletonObjects 二级缓存
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

Object earlySingletonReference = getSingleton(beanName, false);

/** 返回当前的对象
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	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);
                    //如果三级缓存可以获取到,将对象放入二级缓存,删除三级缓存
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

答疑

参考:springIoc依赖注入循环依赖三级缓存_springioc三级缓存-CSDN博客

为什么三级缓存可以解决aop下的循环依赖问题?三级缓存到底有什么作用?


1 在同一个容器中,能否出现同名的不同对象?

不能,id是唯一标识

2,在同一个容器中,按照标准的生命周期,先创建了原始对象,后续又创建了代理对象,那时会怎么办?


当创建了代理对象之后,应该要使用代理对象的,但是原始对象已经存在,那么应该将原始对象给覆盖掉getEarlyBeanReference()方法执行的逻辑是一样的

3 三级缓存到底有什么作用,为什么存在代理对象的时候就要使用三级缓存呢?


在标准的bean的生命周期中,要先创建出原始对象,创建出原始对象之后要使用populateBean方法来完成属性的赋值,此时赋值的对象是原始对象,因为代理对象还没有创建,代理对象的创建步骤是在BeanPostProcessor的后置处理方法中(也就是getEarlyBeanReference中的逻辑),也就是说已经完成赋值之后代理对象才创建出来,所以会报错(this means that said other beans do not use the final version of the bean),如何解决这个问题呢?

需要将代理对象的创建前置,也就是说在对象赋值的那一刻,要唯一性的确定出到底是原始对象还是代理对象,所以会优先把所有的bean都放到三级缓存中在需要进行对象赋值的时候,从三级缓存中取出lambda表达式,lambda表达式的执行逻辑就是确定原始对象还是代理对象。如果是原始对象就赋值原始对象,如果是代理对象就赋值代理对象。

所以说populateBean 执行逻辑在addSingletonFactory方法和 getEarlyBeanReference方法之后

// Initialize the bean instance.
Object exposedObject = bean;
try {

//执行逻辑在addSingletonFactory方法之后
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

4 @Resource注解在Spring中的应用问题

@Resource 注解通常用于注入 Spring 容器中的资源。它不能直接处理循环依赖,因为 Spring 容器在创建 bean 时使用的是懒加载策略,即在实际使用 bean 时才会创建和初始化它。如果两个 bean 相互依赖对方,就会形成循环依赖,这在没有额外配置的情况下是无法解决的。

Spring 提供了 ApplicationContextAware 接口或 @Autowired 注解来处理循环依赖,前提是你需要在 bean 的 setter 方法或构造函数中显式声明依赖。

例如,使用 @Autowired 注解:

javapublic class A {
private B b;

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

public class B {
private A a;

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

在这个例子中,@Autowired 注解告诉 Spring 容器在创建 A 或 B 的实例时,需要注入对方的依赖,这样就可以在构造函数执行时解决循环依赖。

但是,如果你坚持要使用 @Resource 注解来处理循环依赖,这在语义上是不正确的,因为 @Resource 注解主要用于按名称或类型注入资源,而不是用来解决循环依赖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值