别再盲目的说spring有三级缓存了,两个缓存只是启动时为了解决循环依赖,spring启动后只有一个缓存有用
一、什么是循环依赖
循环依赖指的就是循环引用,就是两个或多个 bean 相互之间的持有对方,比如 CircleA 引用 CircleB , CircleB 引用 CircleC, CircleC 引用 CircleA,则它们最终反映为一个环。
不是指循环调用,循环调用是方法之间的环调用,比如
public class TestA {
private TestB testB;
public void a(){
//b方法中testC.c()——>testA.a();
testB.b();
}
public static void main(String[] args) {
new TestA().a();
}
}
循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致StackOverflowError,内存溢出错误。
二、Spring 如何解决循环依赖
Spring容器循环依赖包括构造器循环依赖和 setter循环依赖,那 Spring容器如何解决循环呢?(还是用上面的TestA举例)
1.构造器循环依赖
表示通过构造器注入构成的循环依赖,如
此种情况的循环依赖是无法解决的 ,只能抛出 BeanCurrentlylnCreationException异常 。
可以理解为在准备创建 TestA类时(还未创建), 发现构造方法需要 TestB类,那就去创建 TestB, 在创建 TestB类时又发现 需要 TestC类, 则又去创建 TestC, 最终在创建 TestC时发现又需要 TestA,而最开始的TestA并未被创建,无法使用,所以无法解决。
2. setter循环依赖
表示通过 set注入方式构成的循环依赖 。如
这种方式的循环依赖能解决是通过 Spring 容器 在创建bean之后还未初始化时 ,就注册一个ObjectFactory类持有该bean(如下面代码所示),从而使其他 bean 在引用到该 bean时,通过其ObjectFactory的getObject方法(这里调用了getEarlyBeanReference方法,可以理解为返回了bean)获取到该bean
三、Spring相关源码解析
缓存相关类:重点看AbstractBeanFactory父类DefaultSingletonBeanRegistry,涉及的成员变量有
//第一层缓存 k-> beanName v->bean实例,bean创建并初始化后,会缓存至该map
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//第二层缓存 k-> beanName v->ObjectFactory(ObjectFactory其实就是返回bean实例的引用),bean创建后(初始化之前),会缓存至该map,初始化完成后,从该map移除
//循环依赖相关!!!
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//循环依赖相关!!!
//第三层缓存 k-> beanName v->bean实例,在获取bean时,如果第一层缓存没有,第二层缓存有,就调用对应ObjectFactory的getObject方法,并将返回的bean缓存至该map,从第二层缓存中移除
//(其实就是怕ObjectFactory.getObject是个耗时操作,为了提高性能,调用一次后将结果缓存),初始化完成后,从该map移除
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
//循环依赖相关!!!“当前创建bean池”,bean创建前,会放到该set中,创建完并初始化后,从set中移除
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
创建bean时会调用如下方法(按调用顺序):
1.1 doGetBean:获取bean
BeanFactory和ApplicationContext的getBean方法(还有注解注入bean)最终都会走到这个方法
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//重点1!!!从缓存中获取bean,getSingleton(beanName, true),源码见下
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
...
}
else {
...
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
...
if (mbd.isSingleton()) {
//重点2,在创建bean的前后做一些处理,源码见下
sharedInstance = getSingleton(beanName, () -> {
try {
//重点3,真正去创建bean,会调用doGetBean,源码见下
return createBean(beanName, mbd, args);
}
...
});
...
}
...
return (T) bean;
}
1.2 getSingleton:缓存中获取单例bean
//获取bean时会先调用该方法
//allowEarlyReference,是否允许提前曝光,即从earlySingletonObjects查ObjectFactory
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先看有没有实例缓存
Object singletonObject = this.singletonObjects.get(beanName);
//为null且singletonsCurrentlyInCreation包含beanName,说明该bean正在创建中,比如创建A时,发现引用了B,去创建B,B又引用了A,这样再去创建A时,singletonsCurrentlyInCreation包含了A的beanName
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//比如A引用B,B引用A、C,C也引用A,在创建B时,会调用A的ObjectFactory.getObject()方法获取A,后面创建C时,也需要A,则直接从earlySingletonObjects中get
singletonObject = this.earlySingletonObjects.get(beanName);
//如果没有缓存singletonObject且是要提前曝光
if (singletonObject == null && allowEarlyReference) {
//获取singletonObject
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//缓存,这样就不用再调用singletonFactory.getObject()方法
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
逻辑就是先从实例缓存里拿,没有且bean“正在创建中”(创建A后,设置属性时需要B,去创建B,给B设置属性时又需要A,去创建A时,A就是正在创建中)就从earlySingletonObjects缓存里拿,还没有就从singletonFactories里拿到ObjectFactory,若有,就调用其getObject方法返回bean,并缓存到earlySingletonObjects中
1.3 getSingleton:获取单例bean
这个方法只是个过渡方法,在实际获取bean之前把beanName添加到“当前创建bean池”中,获取之后再从“当前创建bean池”中移除beanName并添加到实例缓存中
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
...
//把beanName加到singletonsCurrentlyInCreation,添加失败就抛BeanCurrentlyInCreationException
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
...
finally {
...
//把beanName从singletonsCurrentlyInCreation中移除,移除失败就抛IllegalStateException
afterSingletonCreation(beanName);
}
if (newSingleton) {
//把单例缓存到singletonObjects
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
1.4 createBean:创建bean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
...
//通过构造器方法反射创建对象,并未初始化,如果设置了构造器引用,如<constructor-arg ref="testb"/>,也会去getBean
instanceWrapper = createBeanInstance(beanName, mbd, args);
...
//单例,且允许循环依赖,且还在创建中,就需要去解决循环依赖,即放入ObjectFactory
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//重点!!!上面介绍过该方法,添加bean对应的ObjectFactory
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 设置bean的属性及调用bean的初始化方法
Object exposedObject = bean;
try {
//获取PropertyValues,遍历初始化然后设值
//会再次调用beanFactory.getBean(refName),比如A中注解引用B,就会调用beanFactory.getBean(BName)获取B实例赋值给A
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
...
return exposedObject;
}
四、流程图
五、个人理解部分
假如我来实现循环依赖的解决,可能是如下代码:
public class MySpring {
//同singletonObjects,缓存beanName和实例
private Map<String,Object> beanInstanceMap=new HashMap<>();
//beanName 和BeanDefinition的缓存
private Map<String, Class> beanDefinationMap=new HashMap<>();
//模仿BeanDefinition中的PropertyValue
private Map<String, String> beanPropertyMap=new HashMap<>();
private void init(){
//注册bean
beanDefinationMap.put("testA", TestA.class);
beanDefinationMap.put("testB", TestB.class);
beanDefinationMap.put("testC", TestC.class);
//设置bean之间的引用关系
beanPropertyMap.put("testA","testB");
beanPropertyMap.put("testB","testC");
beanPropertyMap.put("testC","testA");
}
private Object getBean(String beanName) throws Exception{
//1.先从缓存中获取,
Object instance=beanInstanceMap.get(beanName);
if (instance==null) {
//2.为null就去反射创建实例
instance=beanDefinationMap.get(beanName).newInstance();
//3.先缓存再去填充bean
beanInstanceMap.put(beanName,instance);
//4.拿到配置的属性信息,获取对应bean并设置到bean中
String property=beanPropertyMap.get(beanName);
if (!StringUtils.isEmpty(property)){
Object propertyValue=getBean(property);
setPropertyValue(instance,property,propertyValue);
}
}
return instance;
}
private void setPropertyValue(Object instance, String property, Object propertyValue) throws Exception {
Field field=instance.getClass().getDeclaredField(property);
field.setAccessible(true);
field.set(instance, propertyValue);
}
public static void main(String[] args) throws Exception {
MySpring spring=new MySpring();
spring.init();
TestA testA= (TestA) spring.getBean("testA");
//打印true
System.out.println(testA.getTestB().getTestC().getTestA()==testA);
}
}
这里我只使用了一个缓存就解决了循环依赖,所以产生了如下疑问
- spring为什么额外还使用了earlySingletonObjects 和singletonFactories 两个map,即套了一层ObjectFactory ? 其实就和FactoryBean一样,允许使用者针对这种情况定制化特殊处理,getEarlyBeanReference方法中遍历SmartInstantiationAwareBeanPostProcessor,将处理结果当作bean,导致可能不是原来的引用
- 为什么不创建完了bean就放实例缓存里,而要等装配完成? 因为在initializeBean中,有applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization 方法允许返回别的结果,所以等bean完成全部操作再放入缓存,但是如果在装配时(populateBean)别的bean又引用当前bean,此时实例缓存里没有,不可能再去创建新的,那么就使用中间层ObjectFactory ,虽然不是真正的实例,但是其实差不多,其他bean里的引用是getEarlyBeanReference返回的(其实就是bean),在别的bean创建完成后,当前bean走initializeBean后面的方法,其实exposedObject和bean是一个对象的引用,所以其他bean的里的当前bean也就装配完成了
标签:缓存,Spring,刨析,beanName,Object,bean,源码,singletonObject,创建 From: https://www.cnblogs.com/LiuLance/p/17759884.html