当前位置: 代码网 > it编程>编程语言>Java > Spring中的三级缓存使用及说明

Spring中的三级缓存使用及说明

2025年11月18日 Java 我要评论
常见问题循环依赖循环依赖:就是 n 个类循环(嵌套)使用。简单来说,就是多个 bean 之间互相依赖或引用到对方,最终形成了 闭环。循环依赖的关系可以表示成下图:上图中的 a,b,c 代表三个不同的对

常见问题

循环依赖

循环依赖:就是 n 个类循环(嵌套)使用。

简单来说,就是多个 bean 之间互相依赖或引用到对方,最终形成了 闭环

循环依赖的关系可以表示成下图:

上图中的 a,b,c 代表三个不同的对象,上图表示了三者的引用关系。

循环依赖不仅发生在多个类中,还可能发生在本类中,即 n = 1,这种事极限情况的循环依赖:自己依赖自己

注意:这里指的循环依赖不是方法之间的循环调用,而是对象的相互依赖关系。(因为方法之间的循环调用有出口也能正常运行)

代码示例

public class main {

    public static void main(string[] args) throws exception {
        system.out.println(new a());
    }

}

class a {
    public a() {
        new b();
    }
}

class b {
    public b() {
        new a();
    }
}

运行结果

exception in thread "main" java.lang.stackoverflowerror

从上面代码可以看到,若构造函数之间发生循环依赖(a的构造方法中依赖b,b的构造方法中依赖a),程序会在运行时一直循环调用最终导致内存溢出

spring 中三大循环依赖场景 演示

1.构造器注入循环依赖

@service
public class a {
    public a(b b) {
    }
}
@service
public class b {
    public b(a a) {
    }
}

运行结果

caused by: org.springframework.beans.factory.beancurrentlyincreationexception: error creating bean with name 'a': requested bean is currently in creation: is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.defaultsingletonbeanregistry.beforesingletoncreation(defaultsingletonbeanregistry.java:339)
    at org.springframework.beans.factory.support.defaultsingletonbeanregistry.getsingleton(defaultsingletonbeanregistry.java:215)
    at org.springframework.beans.factory.support.abstractbeanfactory.dogetbean(abstractbeanfactory.java:318)
    at org.springframework.beans.factory.support.abstractbeanfactory.getbean(abstractbeanfactory.java:199)

构造器注入构成的循环依赖是无法解决的!只能抛出

beancurrentlyincreationexception异常表示循环依赖。

错误原因
根本原因是 spring 解决循环依赖依靠的是 bean 的 中间态 这个概念,而中间态指的是 已经实例化,但未初始化的状态。而构造器负责的是实例化,故无法解决构造器循环依赖。

2.field属性注入(setter方法注入)循环依赖

@service
public class a {
    @autowired
    private b b;
}

@service
public class b {
    @autowired
    private a a;
}

最常用的依赖注入方式,不会出现循环依赖问题(省略 setter 方式的注入实现代码,类似)

3.prototype field 属性注入循环依赖

prototype 在平时使用情况较少,仍需重视

@scope(configurablebeanfactory.scope_prototype)
@service
public class a {
    @autowired
    private b b;
}

@scope(configurablebeanfactory.scope_prototype)
@service
public class b {
    @autowired
    private a a;
}

此处代码在运行时不会报错,因为非单例bean默认不会初始化,而是使用时才会初始化

手动 getbean() 或者在一个单例 bean 内 @autowired a 或 b 就会出错

// 在单例bean内注入
    @autowired
    private a a;

注入运行结果

org.springframework.beans.factory.unsatisfieddependencyexception: error creating bean with name 'mytest.testspringbean': unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.unsatisfieddependencyexception: error creating bean with name 'a': unsatisfied dependency expressed through field 'b'; nested exception is org.springframework.beans.factory.unsatisfieddependencyexception: error creating bean with name 'b': unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.beancurrentlyincreationexception: error creating bean with name 'a': requested bean is currently in creation: is there an unresolvable circular reference?

    at org.springframework.beans.factory.annotation.autowiredannotationbeanpostprocessor$autowiredfieldelement.inject(autowiredannotationbeanpostprocessor.java:596)
    at org.springframework.beans.factory.annotation.injectionmetadata.inject(injectionmetadata.java:90)
    at org.springframework.beans.factory.annotation.autowiredannotationbeanpostprocessor.postprocessproperties(autowiredannotationbeanpostprocessor.java:374)

错误解决方案

使用 @lazy:@lazy 只是延迟初始化,当真正需要使用到开始初始化时,依旧会出现上面的异常。

spring 循环依赖小结

1.不能解决的情况:

  • 构造器注入循环依赖
  • prototype filed 属性注入循环依赖

2.能够解决的情况:

  • field 属性注入(setter方法注入)循环依赖

“三级缓存”意义

spring 解决循环依赖原理分析

  • 当获得对象的引用时,对象的属性是可以延迟设置的。但是构造器必须是在获取引用之前,引用由构造器生成,不生成没东西引用。
  • spring 创建 bean 流程

创建 bean 的三个核心方法

  • createbeaninstance:例化,即调用对象的构造方法实例化对象
  • populatebean:填充属性,主要对 bean 的依赖属性注入(@autowired)
  • initializebean:回到一些如initmethod,initalizingbean等方法

由初始化 单例bean 的流程图可以看出,循环依赖主要发生在第二步(populatebean),也就是 field属性注入的处理

spring 容器的“三级缓存”

  • spring 容器的整个生命周期中,单例bean对象是唯一的。即可以使用缓存来加速访问
  • spring 源码中使用了大量的 cache 手段,其中在循环依赖问题的解决过程中就使用了“三级缓存

三级缓存的意义

  • singletonobject:一级缓存,存放完全实例化且属性赋值完成的 bean ,可以直接使用
  • earlysingletonobject:二级缓存,存放早期 bean 的引用,尚未装配属性的 bean
  • singletonfactories:三级缓存,存放实例化完成的 bean 工厂

除了三级缓存,还有另外两个缓存

  • singletonscurrentlyincreation: bean 在创建的过程中都会存储在此,创建完成移出
  • alreadycreated:存放至少被创建一次的 bean,不会重复。即标记 bean 是否创建完成

代码示例

public class defaultsingletonbeanregistry extends simplealiasregistry implements singletonbeanregistry {
	...
	// 从上至下 分表代表这“三级缓存”
	private final map<string, object> singletonobjects = new concurrenthashmap<>(256); //一级缓存
	private final map<string, object> earlysingletonobjects = new hashmap<>(16); // 二级缓存
	private final map<string, objectfactory<?>> singletonfactories = new hashmap<>(16); // 三级缓存
	...
	
	/** names of beans that are currently in creation. */
	// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
	// 它在bean开始创建时放值,创建完成时会将其移出~
	private final set<string> singletonscurrentlyincreation = collections.newsetfrommap(new concurrenthashmap<>(16));

	/** names of beans that have already been created at least once. */
	// 当这个bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
	// 至少被创建了一次的  都会放进这里~~~~
	private final set<string> alreadycreated = collections.newsetfrommap(new concurrenthashmap<>(256));
}

源码解析

  • 单例 bean 源码
public class defaultsingletonbeanregistry extends simplealiasregistry implements singletonbeanregistry {
	...
	@override
	@nullable
	public object getsingleton(string beanname) {
		return getsingleton(beanname, true);
	}
	@nullable
	protected object getsingleton(string beanname, boolean allowearlyreference) {
		//1.先从一级缓存中获取,获取到直接返回
		object singletonobject = this.singletonobjects.get(beanname);
		//2.如果获取不到或对象正在创建,就到二级缓存中去获取,获取到直接返回
		if (singletonobject == null && issingletoncurrentlyincreation(beanname)) {
			synchronized (this.singletonobjects) {
				singletonobject = this.earlysingletonobjects.get(beanname);
				//3.如果仍获取不到,且允许 singletonfactories(allowearlycurrentlyincreation())通过 getobject()获取。
				//就到三级缓存中用 getobject() 获取。
				//获取到就从 singletonfactories中移出,且放进 earlysingletonobjects。
				//(即从三级缓存移动到二级缓存)
				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;
	}
	...
	public boolean issingletoncurrentlyincreation(string beanname) {
		return this.singletonscurrentlyincreation.contains(beanname);
	}
	protected boolean isactuallyincreation(string beanname) {
		return issingletoncurrentlyincreation(beanname);
	}
	...
}

加入 singletonfactories 三级缓存的前提是执行了构造器,所以构造器的循环依赖无法解决。

getsingleton() 从缓存里获取单例对象步骤分析可知,spring 解决循环依赖的重点:在于 singletonfactories这个三级缓存。此 cache 存放着 objectfactory,是解决问题的关键。

// 它可以将创建对象的步骤封装到objectfactory中 交给自定义的scope来选择是否需要创建对象来灵活的实现scope。  具体参见scope接口
@functionalinterface
public interface objectfactory<t> {
	t getobject() throws beansexception;
}

经过 objectfactory.geteobject() 后,此时放进了 二级缓存 earlysingletonobjects 内的这个对象就已经实例化好了,虽然不够完善,但是该对象的引用 已经可以被其他引用了

补充:

二级缓存 earlysingletonobjects 中的数据何时进行 添加、删除?

  • 添加:源码中的 getsingleton() 里从三级缓存移动到二级缓存中(唯一)。
  • 删除:addsingleton、addsingletonfactorie、removesingleton即添加单例、添加单例工厂 objectfactory的时候都会删除二级缓存里面对应的缓存值,是互斥的

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com