三级缓存与循环引用

# 三级缓存与循环引用

# 三级缓存

	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** 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);
1
2
3
4
5
6
7
8

一级缓存singletonObjects用于存放创建好的单实例Bean

二级缓存earlySingletonObjects用于存放早期暴露的单实例Bean,是一个半成品

三级缓存singletonFactories存放的是获得一个实例化Bean的函数(ObjectFactory是一个FunctionalInterface类),这个函数会执行后置处理器SmartInstantiationAwareBeanPostProcessor的后置处理方法,对早期实例化还没有初始化的Bean进行干预。生成这个类的代理类。

# 结合源码查看三级缓存的使用

首先spring通过getBean方法获取Bean

在AbstractBeanFactory#doGetBean方法中,先尝试从缓存中获取Bean,如果能获取到,那么就不用再创建了。

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

		final String beanName = transformedBeanName(name);
		Object bean;

		// 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 + "'");
				}
			}
			//获取实际的Bean,对于工厂Bean,要到工厂Bean的缓存中获取真实的Bean,如果没有,通过FactoryBean.getObject方法获取真实Bean,并放入缓存
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
        else { ... }
        // Check if required type matches the type of the actual bean instance.
        //类型检查
		if (requiredType != null && !requiredType.isInstance(bean)){  ... }

        return (T) bean;
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

查看从缓存中获取Bean的方法,最终调用方法如下

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //从一级缓存中获取已经创建的单实例Bean
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
                //一级缓存中没有,去二级缓存中查看是否有早期暴露的Bean
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
                    //二级缓存中也没有,查看三级缓存
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
                        //如果三级缓存中有,此时得到的是一个能够获得早期实例的函数,通过执行该函数,可以得到早期暴露的实例化Bean
						singletonObject = singletonFactory.getObject();
                        //将执行函数后得到的实例化Bean放到二级缓存,这样如果后面再使用到早期暴露的实例,就可以在二级缓存中获取到,而不是再次执行三级缓存中的函数
                        //因为三级缓存的函数会执行一些后置处理方法,例如AOP代理会生成一个早期实例的代理实例,如果重复执行,那么就获取了新的代理,就没法保证单例性质。
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

如果三级缓存都没有获得Bean,那么就需要进入Bean的创建流程 将正在的创建流程封装成函数方法,在getSingleton中执行该函数方法创建Bean,getSingleton方法会对创建好的Bean做一些其他处理,这个下文还会提到。

// Create bean instance.
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                destroySingleton(beanName);
                throw ex;
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

先看createBean方法

spring会先用Bean的构造方法实例化一个Bean,此时Bean还没有初始化,我们叫他早期Bean。

先将早期Bean封装成可以执行后置处理的函数,放到三级缓存中

然后再进入Bean的初始化流程

然后再次从缓存中获取,这时一二级缓存中是没有实例的,只有三级缓存中存放了可以获取实例的函数

执行这个函数,从三级缓存中获取Bean,并放到二级缓存。

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @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);
		}
		
        ...

		// 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");
			}
            //放入三级缓存
            //所以Bean是先实例化,然后封装成函数,放入三级缓存的。
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
        //初始化Bean  自动装配,执行初始化方法等
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			...
		}

		if (earlySingletonExposure) {
            //这里传入的是false,如果二级缓存中也为空,说明三级缓存中的函数没有被调用过,既没有发生循环引用,那么走到这里的对象已经是代理对象了,不会再去三级缓存中找
			//如果二级缓存不为空,说明在初始化的时候已经由于其他Bean的初始化调用了三级缓存中的函数,既发生了循环引用,那么这里使用二级缓存中的对象替换当前对象
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					...
                }
			}
		}
        ...

		return exposedObject;
	}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

此时一级缓存三级缓存没有这个实例,二级缓存中有。

再回过头来看看getSingleton方法

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) {
            ...
            try {
                //调用这个封装了Bean创建流程的函数
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            } catch ...
            
            if (newSingleton) {
                //添加到一级缓存,清除二级缓存和三级缓存
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

到此,三级缓存的流程结束。Bean从三级缓存到二级缓存最后存放到一级缓存,也就是单例池中。

# 循环引用

简单举例就是:

@Servie
public class A {
    @Autowired
    private B b;
}
@Servie
public class B {
    @Autowired
    private A a;
}
1
2
3
4
5
6
7
8
9
10

假设没有三级缓存,只有一个单例池用于存放创建好的Bean

按照上文提到的Bean创建流程,先通过反射调用构造函数创建A实例,然后初始化A,自动注入B,getBean获取B,发现B不在单例池中,那么进入B的创建流程

通过反射调用构造函数创建B实例,然后初始化B,自动注入A,getBean获取A,发现A不在单例池中,那么进入A的创建流程

由于A与B的相互引用,导致spring在创建Bean的时候陷入死循环。

为了解决这个问题,spring引入了三级缓存,通过早期暴露出只是实例化还未初始化的Bean,来解决死循环问题。

这样,在最开始创建完A后,暴露到三级缓存中,在进入B的创建流程后,B初始化注入A时就可以从三级缓存中获取到。

这样一看,似乎只需要两个缓存也可以解决循环引用问题,那么Spring为什么要用三个缓存呢?

这要从第三级缓存singletonFactories的作用说起。

从上文源码中可以知道三级缓存存放的实际上是一个获取早期Bean的函数

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}
1
2
3
4
5
6
7
8
9
10
11
12

实际上,从上文可以知道早期Bean并不是在这个方法里创建的,这个方法只是对刚实例化的Bean进行一些后置处理。其中拿AOP举例,经过后置处理后返回的其实是代理对象。

所以第三级缓存的作用也就是允许执行一些后置处理,得到一个不是原对象的代理对象。

同时,三级缓存中的函数只能执行一次,不然多次调用,产生多个代理,必然违反单例原则,所以三级缓存中的函数执行完会立即放到二级缓存中。

那么我们可能会想,在一开始实例化A后,就直接进行后置处理,得到代理对象存放在二级缓存中,这样就不需要第三级缓存了,这样确实可以,但是并不是所有的Bean都存在循环引用的问题,这样处理,所有的Bean都会在构造完成后就生成代理对象,这违背了Spring的设计原则。而使用三级缓存,只针对真的发生了循环引用的Bean提前暴露代理对象,显得稍微优雅一些。

上次更新: 2023/04/09, 16:34:32
最近更新
01
go-admin-ui项目仿写练手1-登录页
06-29
02
maven依赖问题
06-17
03
JVM相关命令
02-21
更多文章>