# SpringBoot(2.7.x) 三级缓存介绍

149 阅读9分钟

这篇文章从代码的角度过一遍三级缓存,三级缓存,其实就是bean的创建过程。这里只介绍单例模式,原型模式有机会再单独介绍

创建bean的入口getBean()方法

这里getBean()方法,默认指的是DefaultListableBeanFactory的getBean()方法。这里我们说的三级缓存,其实是我们自己的叫法,人家源码压根就没有"缓存"这样的命名,三级缓存其实是bean创建时所需要经过的是三个容器

  • Map<String, Object> singletonObjects 单例对象池(一级缓存)
    • 已经创建完成的Bean对象,会被存到这里
  • Map<String, Object> earlySingletonObjects 早期单例对象池(二级缓存)
    • 从singletonFactories拿出来的还未完成创建的Bean,会存到这里
  • Map<String, ObjectFactory<?>> singletonFactories 单例工厂(三级缓存)
    • 创建bean过程中,执行属性填充的时候,发现依赖其他的对象,就会把当前创建的对象,暴露到singletonFactories

为了防止下面创建的过程大家听得一头雾水,这里先明确两个概念"实例化"和"初始化",这里会简单粗暴的把基础的概念告诉你,但是它具体是个什么东西,请自行看源码

  • 实例化
    • new ClassA() 这种创建了一个对象的,就称之为实例化。(Spring中实例化,用的是反射,但是大同小异,反正这个对象被构建出来了)
  • 初始化
    • ClassA a = new ClassA(); 这里是实例化
    • a.setName()
    • a.init()
    • 这种给你的实例化对象赋值和执行初始化方法的过程,叫做初始化(当然你也可以把赋值和执行初始化方法分开,根据你看源码的理解自行理解)
    • 一个对象初始化完,其实就是执行完init方法,已经没有什么需要填充或者执行delete

我们要说的是单例模式,所以这些bean,只能创建第一次。如果存在多个类依赖同一个对象,或者出现了循环依赖,要怎么处理呢?Spring就通过上面的三个容器,去解决这些问题。

getSingleton()获取单例对象

获取单例对象。很明显,我们的bean是一个单例,所以我们需要获取这个单例。那么这个单例,是不能被重复创建的,如果我获取这个单例的时候,这个单例没创建,或者在创建中怎么办?带着问题我们去看Spring是怎么实现的

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		// 先去单例池里找,看下有没有创建好的单例bean可以直接拿
		Object singletonObject = this.singletonObjects.get(beanName);
		// 没有创建好的,而且这个bean正在创建中(isSingletonCurrentlyInCreation(beanName))
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		  // 去早期的单例池里获取单例bean
			singletonObject = this.earlySingletonObjects.get(beanName);
			// 还是没有,allowEarlyReference(是否允许早期暴露)
			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) {
							// 执行getObjec()方法,拿到对象
								singletonObject = singletonFactory.getObject();
								// 放入早期单例池
								this.earlySingletonObjects.put(beanName, singletonObject);
								// 从单例工厂删除
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		// 返回拿到的单例对象(可以为null)
		return singletonObject;
	}

这里的目的就是为了从单例池里获取到一个对象,这里说下singletonFactory这里为什么调用了getObject()方法

@FunctionalInterface
public interface ObjectFactory<T> {

	T getObject() throws BeansException;

}

因为ObjectFactory是一个lambda表达式,getObject()后返回的,才是真实的对象

可以看到,我们只是去拿了一下单例bean,如果拿不到,那就要进入创建bean的流程

另一个getSingleton(String beanName, ObjectFactory<?> singletonFactory)

上面的那个getSingleton()的作用是拿一个单例bean,有就返回单例,没有就返回null,这个方法,是拿一个单例,没有就创建一个单例返回 这里传入了一个ObjectFactory,其实也就是我们单例工厂(三级缓存存的值)

先看一下入口方法:

@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
getSingleton(beanName, () -> {
						try {
						// 内部lambda表达式,直接调用的createBean()方法
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
	}

那么真正的创建bean的方法,就在createBean()里面了

createBean()

try {
	// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
	// 内部调用InstantiationAwareBeanPostProcessor,让你可以这个地方,返回一个对象(或者代理对象)。
	Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
	if (bean != null) {
		return bean;
	}
}
// 没有返回代理兑现的时候,走的真实的创建方法
Object beanInstance = doCreateBean(beanName, mbdToUse, args);

下面贴出主干代码,中间会有省略,需要的自行查看源码。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
			// Instantiate the bean.
		  // 实例化bean
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
		// 这里是看下哟没有实例化的缓存
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
		// 这里是实例化对象的地方,返回的是已经实例化好的对象的包装类
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		// 从包装类中拿出真实的对象
			Object bean = instanceWrapper.getWrappedInstance();
			
			}
		// 判断是否需要提前暴露,需要的话,加入单例工厂
		// 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");
			}
			// 加入单例工厂(也就是我们说的三级缓存)
			// 这里可以看到,这里其实是把一个lambda表达式放入了map,当你需要用的时候,就会去执行getEarlyBeanReference
			// 当你从SingletonFactory拿到了lambda表达式执行完以后,就会放入earlySingletonObjects.put,这时返回的,也可能是个代理对象
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		
		// Initialize the bean instance.
		// 初始化bean
		Object exposedObject = bean;
		try {
		  // 填充属性
		  // 填充属性的时候,会判断属性是否有对象,有的话就走getBean()方法,递归去创建bean
			populateBean(beanName, mbd, instanceWrapper);
			// 执行初始化方法,内部会调用BeanPostProcessor的实现类(代理对象可以在这里被创建)
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		
		// Register bean as disposable.
		// 执行完以后
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}

// 直接返回当时传入的bean,或者走SmartInstantiationAwareBeanPostProcessor的实现方法,返回一个新的bean
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		// 就是这里,可以返回一个代理对象
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
		return exposedObject;
	}

细心的学可能注意到了(也可能没注意到),上面有三个钩子可以让你创建代理对象。

  • 实现InstantiationAwareBeanPostProcessor.class,让你不走doCreateBean,直接返回一个对象(或者代理对象)
    • 约等于强制替换,创建bean的时候,从InstantiationAwareBeanPostProcessor.class拿到了bean就直接返回这个bean,不继续走下面的流程了
  • 实现SmartInstantiationAwareBeanPostProcessor.class,让你在从singletonFatory中拿bean对象的时候,返回一个对象(或者代理对象)
    • 解决创建过程中,代理对象的问题
    • 在有依赖关系的bean创建中,提前暴露当前bean,然后在需要注入到其他类的时候,执行一次SmartInstantiationAwareBeanPostProcessor.class,把返回的值放入earlySingletonObjects
    • 这里的做法目的是,如果有代理对象,在单例模式下,不会重复去创建这个代理对象,也保证被依赖的Bean可以直接拿到代理对象
  • 实现BeanPostProcessor.class,让你在initializeBean()内,执行invokeInitMethods()前后,可以返回一个对象(或者代理对象)
    • 这里就是正常初始化好bean以后,看下需不需要返回代理对象或者替换掉当前的bean

这里Spring给你同时照顾到了几种情况

  • 假如一个项目中,我需要替换掉一个bean,自己自定义的bean,那么我可以在InstantiationAwareBeanPostProcessor.class的时候,直接给它替换成自己需要的bean返回,不走它后面的创建流程
  • 当有依赖关系时(BCD依赖A),那么被依赖的bean(BCD),通过earlySingletonObjects可以直接拿到A的代理对象
  • 当需要给初始化好的bean做增强时,可以通过实现BeanPostProcessor.class,对原本完整的bean 做增强。

最后通过public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)这个方法创建的新bean,会加入singletonObjects和registeredSingletons,从singletonFactories和earlySingletonObjects删除

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

总结

整个bean的创建流程到这里就差不多了,这里没有贴太多细节出来,因为整个bean的创建过程是非常复杂的。这里就说说创建一个新的单例bean如果经过三级缓存,是怎么一个流程

  • 首先,创建bean的时候,会先去三级缓存中拿,三级缓存中拿不到就会去创建
    • 拿的顺序是,一级、二级、三级
    • 三级拿到的是一个lambda表达式,执行lambda表达式的默认方法,会返回bean或者bean的代理对象,这里返回的值,会放入二级缓存,然后从三级缓存删除,避免重复去执行lambda表达式,返回新的对象,毕竟单例bean就应该只被创建一次
    • 然后这里做一个勘误,虽然拿的方式是一级、二级、三级,但是从二级拿的bean不会直接放到一级,bean只有完全按创建完,才会放入一级,然后删掉二级和三级,没有从二级拿到一级的操作
  • 创建的过程中,如果当前的bean有依赖,就会提前把自己暴露到三级缓存,然后去创建依赖的bean
    • 如果依赖的bean里面还有依赖,就会继续暴露当前的bean,继续创建依赖的bean,就这样一直递归创建
    • 如果有循环依赖A->B,B->A这种情况
      • 由于A依赖B,A会先暴露到三级,然后去创建B,B创建时发现依赖A,也会暴露到三级,然后去拿A
      • B通过执行A的提前暴露lambda表达式,可以拿到A或者A的代理对象,然后把A或A的代理放到二级缓存
      • B拿到A以后,继续创建流程,B创建完以后,把自己放入一级缓存和registeredSingletons,删除二级、三级缓存,然后返回给A
    • A在创建完B以后,继续执行创建流程,A创建完以后,把自己放入一级缓存和registeredSingletons,删除二级、三级缓存
  • 这样就是一个完整创建过程了