当前位置 : 主页 > 编程语言 > java >

Spring5——三级缓存和循环依赖

来源:互联网 收集:自由互联 发布时间:2023-02-04
一、Spring 三级缓存 1、三级缓存的定义 看源码的DefaultSingletonBeanRegistry中有三个Map对象,通常情况下,称singletonObjects为一级缓存,earlySingletonObjects为二级缓存,singletonFactories 为三级缓存

一、Spring 三级缓存

1、三级缓存的定义

看源码的DefaultSingletonBeanRegistry中有三个Map对象,通常情况下,称singletonObjects为一级缓存,earlySingletonObjects为二级缓存,singletonFactories 为三级缓存。

等级 名称 说明 一级 singletonObjects 可以理解为单例池 二级 earlySingletonObjects 早期单例对象缓存 三级 singletonFactories 单例工厂缓存 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { /** Cache of singleton objects: bean name to bean instance. */ //一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ //三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性 // 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory, // 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ //二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象 // 执行了工厂方法生产出来的Bean,bean被放进去之后, // 那么当bean在创建过程中,就可以通过getBean方法获取到 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);、 /** Names of beans that are currently in creation. */ //三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); }

2、三级缓存的作用

名称 作用 singletonObjects 存放初始化后的单例对象,也就是完成的bean对象 earlySingletonObjects 存放实例化,未完成初始化的单例对象(未完成属性注入的对象),也是用来解决性能问题 singletonFactories 存放ObjectFactory对象,存放的是工厂对象,主要用来解决循环依赖和aop的问题

3、为什么需要第三级缓存?

实际上使用二级缓存就能够实现循环依赖的解决,将二级缓存用来存放早期单例对象(半成品),然后一样的走生命周期的流程,也没有问题,为什么还需要三级缓存?

 

这个问题反映了spring设计的相当的精妙,如果说,注入的对象实现了aop功能,那么通过循环依赖注入其他bean的时候,bean是原始对象,也就是说没有经过aop的处理,不是最终的代理对象,需要通过三级缓存的ObjectFactory对象工厂才能解决aop的问题,才能获得最终需要的代理对象。

二、循环依赖是如何解决的

假设情景A依赖于B,B依赖于A

 

首先A通过getBean调到doCreateBean(Spring通过getBean方法获取bean),初步实例化即调用createBeanInstance()方法后,属性注入之前进入到下边代码中

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { //省略前面的部分代码 // even when triggered by lifecycle interfaces like BeanFactoryAware. // 7.判断是否需要提早曝光实例:单例 && 允许循环依赖 && 当前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"); } // 8.提前曝光beanName的ObjectFactory,用于解决循环引用 // 8.1 应用后置处理器SmartInstantiationAwareBeanPostProcessor,允许返回指定bean的早期引用,若没有则直接返回bean addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. // 初始化bean实例。 Object exposedObject = bean; try { // 9.对bean进行属性填充;其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean实例 populateBean(beanName, mbd, instanceWrapper); // 10.对bean进行初始化 //初始化bean,过程如下: //1:判断是否实现了BeanNameAware,BeanClassLoaderAware, // BeanFactoryAware方法,如果有,则设置相关的属性 //2: 调用bean初始化的前置(BeanPostProcessor)操作 //3: 执行初始化的方法。 // 如果有initializingBean,则调用afterPropertiesSet // 如果有InitMethod,则调用初始方法 //4: 调用bean初始化的后置(BeanPostProcessor)操作 exposedObject = initializeBean(beanName, exposedObject, mbd); } //省略中间的部分代码 // 13.完成创建并返回 return exposedObject; } }

属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中,即调用addSingletonFactory()方法

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // 这里传入的参数也是一个lambda表达式,() -> getEarlyBeanReference(beanName, mbd, bean) protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { // 1.如果beanName不存在于singletonObjects缓存中 if (!this.singletonObjects.containsKey(beanName)) { //往三级缓存里添加 // 2.将beanName和singletonFactory注册到singletonFactories缓存(beanName -> 该beanName的单例工厂) this.singletonFactories.put(beanName, singletonFactory); //清除此Bean在二级缓存里的缓存信息 // 3.移除earlySingletonObjects缓存中的beanName(beanName -> beanName的早期单例对象) this.earlySingletonObjects.remove(beanName); //这里为了记录注册单例的顺序 // 4.将beanName注册到registeredSingletons缓存(已经注册的单例集合) this.registeredSingletons.add(beanName); } } } }

这里只是添加了一个工厂(通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象),执行工厂的getObject相当于执行getEarlyBeanReference。那么,什么时候会去调用这个工厂的getObject方法呢?

 

当A开始属性注入,B开始实例化时,同样会通过getBean,同样会跟A一样属性注入,再次实例化A,此时再次实例化A时,getBean经过getSingleton会从三级缓存中拿出ObjectFactory,调用getObject会拿到A的对象。

 

到此为止,循环依赖被解决。

 

解决循环依赖后,会在doCreateBean()方法中调用populateBean()方法填充属性,完成对bean的依赖属性进行注入(@Autowired)。

 

即在AbstractBeanFactory类中:

  • 通过 getBean(String name)方法、调用doGetBean()方法、在doGetBean()方法内部调用Object sharedInstance = getSingleton(beanName);代码,在getSingleton()方法中会从三级缓存中拿出ObjectFactory

  • 通过ObjectFactory单例对象工厂创建的单例对象,放到早期单例对象缓存中即二级缓存earlySingletonObjects,并在三级缓存中移除beanName对应的单例对象工厂

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { //一级缓存:单例对象缓存池,beanName->Bean,其中存储的就是实例化,属性赋值成功之后的单例对象 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //三级缓存:单例工厂的缓存,beanName->ObjectFactory,添加进去的时候实例还未具备属性 // 用于保存beanName和创建bean的工厂之间的关系map,单例Bean在创建之初过早的暴露出去的Factory, // 为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); //二级缓存:早期的单例对象,beanName->Bean,其中存储的是实例化之后,属性未赋值的单例对象 // 执行了工厂方法生产出来的Bean,bean被放进去之后, // 那么当bean在创建过程中,就可以通过getBean方法获取到 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); //三级缓存是用来解决循环依赖,而这个缓存就是用来检测是否存在循环依赖的 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1.从单例对象缓存中获取beanName对应的单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 2.如果单例对象缓存中没有,并且该beanName对应的单例bean正在创建中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { //尝试给一级缓存对象加锁,因为接下来就要对缓存对象操作了 synchronized (this.singletonObjects) { // 4.从早期单例对象缓存中获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里 // 的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作) //尝试从二级缓存earlySingletonObjects这个存储还没进行属性添加操作的Bean实例缓存中获取 singletonObject = this.earlySingletonObjects.get(beanName); // 5.如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用 //如果还没有获取到并且第二个参数为true,为true则表示bean允许被循环引用 if (singletonObject == null && allowEarlyReference) { // 6.从单例工厂缓存中获取beanName的单例工厂 //从三级缓存singletonFactories这个ObjectFactory实例的缓存里尝试获取创建此Bean的单例工厂实例 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); //如果获取到工厂实例 if (singletonFactory != null) { // 7.如果存在单例对象工厂,则通过工厂创建一个单例对象 singletonObject = singletonFactory.getObject(); // 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了, // 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂 this.singletonFactories.remove(beanName); } } } } // 10.返回单例对象 return singletonObject; } }

循环依赖确实被解决了,但是从过程看,似乎ObjectFactory缓存没什么存在的必要?似乎singletonObjects和earlySingletonObjects已经可以解决依赖问题了?

 

那么三级缓存到底有什么作用呢?我们再仔细分析下singletonFactories

 

在doCreateBean()方法调用addSingletonFactory()方法,提前曝光beanName的ObjectFactory,用于解决循环引用的时候,会调用getEarlyBeanReference()方法,即addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));方法的时候

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; // 1.如果bean不为空 && mbd不是合成 && 存在InstantiationAwareBeanPostProcessors if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { // 2.应用所有SmartInstantiationAwareBeanPostProcessor,调用getEarlyBeanReference方法 if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; // 3.允许SmartInstantiationAwareBeanPostProcessor返回指定bean的早期引用 exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } // 4.返回要作为bean引用公开的对象,如果没有SmartInstantiationAwareBeanPostProcessor修改,则返回的是入参的bean对象本身 return exposedObject; } }

它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator。也就是说如果在不考虑AOP的情况下,上面的代码等价于:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; return exposedObject; } }

也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP的情况下三级缓存有用嘛?讲道理,真的没什么用。

结合了AOP的循环依赖

上面已经说过了,在普通的循环依赖的情况下,使用二级缓存也能解决循环依赖,那么三级缓存就显得可有可无了。其实、三级缓存实际上跟Spring中的AOP相关,我们再来看一看getEarlyBeanReference的代码:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; // 1.如果bean不为空 && mbd不是合成 && 存在InstantiationAwareBeanPostProcessors if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { // 2.应用所有SmartInstantiationAwareBeanPostProcessor,调用getEarlyBeanReference方法 if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; // 3.允许SmartInstantiationAwareBeanPostProcessor返回指定bean的早期引用 exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } // 4.返回要作为bean引用公开的对象,如果没有SmartInstantiationAwareBeanPostProcessor修改,则返回的是入参的bean对象本身 return exposedObject; } }

如果在开启AOP的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreator的父类的AbstractAutoProxyCreator的getEarlyBeanReference方法,对应的源码如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象 return wrapIfNecessary(bean, beanName, cacheKey); } }

wrapIfNecessary为Spring实现Bean代理的核心方法

  • wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,另一处是postProcessAfterInitialization
  • 在wrapIfNecessary方法内部调用getAdvicesAndAdvisorsForBean()返回匹配当前Bean的所有Advice\Advisor\Interceptor,用于判断此该类是否需要创建代理。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { /** * Spring实现Bean代理的核心方法。wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference, * 另一处是postProcessAfterInitialization */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //已经被处理过 // 1.判断当前bean是否在targetSourcedBeans缓存中存在(已经处理过),如果存在,则直接返回当前bean if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } //不需要被织入逻辑的 // 2.在advisedBeans缓存中存在,并且value为false,则代表无需处理 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } //是不是基础的bean 是不是需要跳过的 // 3.bean的类是aop基础设施类 || bean应该跳过,则标记为无需处理,并返回 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. // 返回匹配当前Bean的所有Advice\Advisor\Interceptor Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); // 5.如果存在增强器则创建代理 if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //创建Bean对应的代理,SingletonTargetSource用于封装实现类的信息 // 5.1 创建代理对象:这边SingletonTargetSource的target属性存放的就是我们原来的bean实例(也就是被代理对象), // 用于最后增加逻辑执行完毕后,通过反射执行我们真正的方法时使用(method.invoke(bean, args)) Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); // 5.2 创建完代理后,将cacheKey -> 代理类的class放到缓存 this.proxyTypes.put(cacheKey, proxy.getClass()); // 返回代理对象 return proxy; } //该Bean是不需要进行代理的,下次就不需要重复生成了 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } }

回到上面的例子,我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。

 

三级缓存的意义何在?

 

就以我们上的A、B为例,其中A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程

假设不使用三级缓存,直接用二级缓存中,也是可以实现的,那么三级缓存搭配二级缓存的存在有什么意义呢?

上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言,消耗的时间是一样的(所以常见的三级缓存提高了效率这种说法都是错误的)

 

上述这种情况下,差别就是在哪里创建代理。如果不用三级缓存,使用二级缓存,违背了Spring在结合AOP跟Bean的生命周期的设计!Spring结合AOP跟Bean的生命周期本身就是通过AbstractAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

Spring Bean的生命周期:https://blog.51cto.com/u_14014612/5995566

 

参考: https://blog.csdn.net/qq_38762237/article/details/108468333

https://juejin.cn/post/6930904292958142478#heading-5

https://www.cnblogs.com/juniorMa/p/14087517.html

上一篇:Spring5——Spring事务原理
下一篇:没有了
网友评论