Spring三级缓存机制

hello!又到了周末了,最近的自己又偷懒了,最近新学的知识点很少,嗯,今天坐在工位上,尽然思考半天,准备发一篇文章的,但是,这一周一直在写项目(公司的和自己的),我对技能的技术提升这块好像没有太多,比如mmp的算法…哈哈哈~!!垃圾哦,我就复习了插入,选择,冒泡(惭愧啊)

!一不小心又说废话了来,进入正题啦!

Spring三级缓存机制

一.什么是三级缓存

用我们自己的大白话:

就是防止套套娃,防止几个bean的互相引用对方,产生像OS闭环死锁问题

举个粟子:

A应用B,B引用C,C引用A,产生一个闭环依赖的问题 (这也是一个典型的依赖问题,面试题里面也常有哦)

在这里插入图片描述

二.源码剖析

  • 在剖析源码,我么首先要知道和三级缓存的最关联的地方

  • 步骤:

    • 随便打开一个Spring的项目
    • shift键(全局搜索),在弹窗中输入AbstractAutowireCapableBeanFactory

在这里插入图片描述

  • Ctrl+F搜索doCreateBean方法

在这里插入图片描述

doCreatBean的源码: 包含了三级缓存、属性注入、初始化

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
        }

        if (instanceWrapper == null) {
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);
        }

        Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        synchronized(mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                } catch (Throwable var17) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);
                }

                mbd.postProcessed = true;
            }
        }

        boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
        if (earlySingletonExposure) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
            }

            this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
        }

        Object exposedObject = bean;

        try {
            this.populateBean(beanName, mbd, instanceWrapper);
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable var18) {
            if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
                throw (BeanCreationException)var18;
            }

            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
        }

        if (earlySingletonExposure) {
            Object earlySingletonReference = this.getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                } else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
                    String[] dependentBeans = this.getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
                    String[] var12 = dependentBeans;
                    int var13 = dependentBeans.length;

                    for(int var14 = 0; var14 < var13; ++var14) {
                        String dependentBean = var12[var14];
                        if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }

                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

        try {
            this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
            return exposedObject;
        } catch (BeanDefinitionValidationException var16) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
        }
    }

2.1三级缓存是哪几层呢?

  1. singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  2. earlySingletonObjects:提前曝光的单例对象cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  3. singletonFactories单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

源码:在DefaultSingletonBeanRegistry的类中

	// 从上至下 分表代表这“三级缓存”
    //一级缓存
	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); 

一级缓存

singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用

一级缓存里存的是成品对象,实例化和初始化都完成了,我们的应用中使用的对象就是一级缓存中的

二级缓存

二级缓存中存的是半成品,没有完成属性注入和初始化,用来解决对象创建过程中的循环依赖问题
早期暴露出去的Bean,其实也就是解决循环依赖的Bean。早期的意思就是没有完完全全创建好,但是由于有循环依赖,就需要把这种Bean提前暴露出去。其实 早期暴露出去的Bean 跟 完完全全创建好的Bean 他们是同一个对象,只不过早期Bean里面的注解可能还没处理,完完全全的Bean已经处理了完了,但是他们指的还是同一个对象,只不过它们是在Bean创建过程中处于的不同状态

三级缓存

三级缓存中存的是 ObjectFactory<?> 类型的代理工厂对象,用于处理存在 AOP 时的循环依赖问题
存的是每个Bean对应的ObjectFactory对象,通过调用这个对象的getObject方法,就可以获取到早期暴露出去的Bean。
注意:这里有个很重要的细节就是三级缓存只会对单例的Bean生效,像多例的是无法利用到三级缓存的,通过三级缓存所在的类名DefaultSingletonBeanRegistry就可以看出,仅仅是对SingletonBean也就是单例Bean有效果。2.2对象的创建过程(可以不看~补充)

2.2.1首先是调用AbstractBeanFactory 中的 doGetBean()方法

也就是上面我们的看到的源码,对一个bean进行初始化的操作

2.2.2DefaultSingletonBeanRegistry中的 getSingleton()方法–(核心)

解释下:(三级缓存的核心)

  1. 先从一级缓存singletonObjects中去获取,若获取到就直接return

  2. 若获取不到或者是对象正在创建中(isSingletonCurrentlyInCreation()),(推荐直接看下面的源码哈!~)

    那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)

  3. 如果还是获取不到,且允许singletonFactoriesallowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。

    如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects

    其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)此处的移动保证了,之后在init时候仍然是同一个对象

源码:

 @Nullable
    public Object getSingleton(String beanName) {
        return this.getSingleton(beanName, true);
    }

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //先从一级缓存`singletonObjects`中去获取,若获取到就直接`return`
        Object singletonObject = this.singletonObjects.get(beanName);
        //若获取不到或者是对象正在创建中
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            //那就再从二级缓存`earlySingletonObjects`中获取,如果获取到就直接return
            singletonObject = this.earlySingletonObjects.get(beanName);
            //如果还是获取不到,且允许`singletonFactories`,`allowEarlyReference=true`通过`getObject()`获取。
            if (singletonObject == null && allowEarlyReference) {
                synchronized(this.singletonObjects) {
                    //这块有点小懵逼的,建议debug来看
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        //singletonObject为空情况下,我们去earlySingletonObjects中获取bean
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                //冲冲判断,在getobject中进行获取
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }
2.2.3AbstractAutowireCapableBeanFactoryCreateBean

实例化

  1. 使用构造器/工厂方法 instanceWrapper是一个BeanWrapper

    instanceWrapper = createBeanInstance(beanName,mbd args);
    
  2. 此处bean为"原始Bean" 也就是这里的A实例对象:service_a@666

    final Object bean = instanceWrapper.getWrappedInstance();
    

添加到三级缓存

  1. 允许暴露,就把A绑定在ObjectFactory上,注册到三级缓存singletonFactories里面去保存着

  2. 解释下:~这里后置处理器getEarlyBeanReference方法会被促发,自动代理创建器在此处创建代理对象(注意执行时机是在 为执行三级缓存的时候)

    if (earlySingletonExposure) {
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
     }
    
  3. 注意! 此时加入的是一个工厂都没有执行

createBean的源码

 public <T> T createBean(Class<T> beanClass) throws BeansException {
        RootBeanDefinition bd = new RootBeanDefinition(beanClass);
        bd.setScope("prototype");
        bd.allowCaching = ClassUtils.isCacheSafe(beanClass, this.getBeanClassLoader());
        return this.createBean(beanClass.getName(), bd, (Object[])null);
    }
2.2.4属性赋值

此时候上面说到的getEarlyBeanReference方法就会被执行。

这也解释为何我们@Autowired是个代理对象,而不是普通对象的根本原因(平时我们在service注入时贼喜欢用的)

populateBean(beanName, mbd, instanceWrapper);

解决循环依赖,在获取单例对象singletonFactory.getObject()调用了,(什么???不想看了?喵的,别呀,就要完事了~)

SmartInstantiationAwareBeanPostProcessor:继承自BeanPostProcessor

BeanPostProcessor :接口会在init阶段生成对对象的代理getCacheKey保证不会重复生成代理对象

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    @Nullable
    protected static final Object[] DO_NOT_PROXY = null;
    protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0];
    protected final Log logger = LogFactory.getLog(this.getClass());
    private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
    private boolean freezeProxy = false;
    private String[] interceptorNames = new String[0];
    private boolean applyCommonInterceptorsFirst = true;
    @Nullable
    private TargetSourceCreator[] customTargetSourceCreators;
    @Nullable
    private BeanFactory beanFactory;
    private final Set<String> targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap(16));
    
    //主要看这一行earlyProxyReferences
    private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap(16);
    private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap(16);
    private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap(256);
    
    //getEarlyBeanReference在获取(这源码写的真nice!!!!)
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        return this.wrapIfNecessary(bean, beanName, cacheKey);
    }
    

2.2.5初始化对象(摘抄来的哈~!!!)
      exposedObject = initializeBean(beanName, exposedObject, mbd);
			.....
                
     // 至此,相当于A@1234已经实例化完成、初始化完成(属性也全部赋值了~)
	// 这一步我把它理解为校验:校验:校验是否有循环引用问题~~~~~

	if (earlySingletonExposure) {
		// 注意此处第二个参数传的false,表示不去三级缓存里singletonFactories再去调用一次getObject()方法了~~~
		// 上面建讲到了由于B在初始化的时候,会触发A的ObjectFactory.getObject()  所以a此处已经在二级缓存earlySingletonObjects里了
		// 因此此处返回A的实例:A@1234
		Object earlySingletonReference = getSingleton(beanName, false);
		if (earlySingletonReference != null) {
		
			// 这个等式表示,exposedObject若没有再被代理过,这里就是相等的
			// 显然此处我们的a对象的exposedObject它是没有被代理过的  所以if会进去~
			// 这种情况至此,就全部结束了~~~
			if (exposedObject == bean) {
				exposedObject = earlySingletonReference;
			}
	
			// 继续以A为例,比如方法标注了@Aysnc注解,exposedObject此时候就是一个代理对象,因此就会进到这里来
			//hasDependentBean(beanName)是肯定为true,因为getDependentBeans(beanName)得到的是["b"]这个依赖
			else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				String[] dependentBeans = getDependentBeans(beanName);
				Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);

				// A@1234依赖的是["b"],所以此处去检查b
				// 如果最终存在实际依赖的bean:actualDependentBeans不为空 那就抛出异常  证明循环引用了~
				for (String dependentBean : dependentBeans) {
					// 这个判断原则是:如果此时候b并还没有创建好,this.alreadyCreated.contains(beanName)=true表示此bean已经被创建过,就返回false
					// 若该bean没有在alreadyCreated缓存里,就是说没被创建过(其实只有CreatedForTypeCheckOnly才会是此仓库)
					if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
						actualDependentBeans.add(dependentBean);
					}
				}
				if (!actualDependentBeans.isEmpty()) {
					throw new BeanCurrentlyInCreationException(beanName,
							"Bean with name '" + beanName + "' has been injected into other beans [" +
							StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
							"] in its raw version as part of a circular reference, but has eventually been " +
							"wrapped. This means that said other beans do not use the final version of the " +
							"bean. This is often the result of over-eager type matching - consider using " +
							"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
				}
			}
		}
	}

3.面试题:(思考)

3.1什么是三级缓存?

要答案?看前面的(仔细)

3.2Spring是如何解决循环依赖的?

我们通过A实例依赖B,B实例依赖A的例子来分析具体流程:

1、A对象实例化之后,属性注入之前,其实会把A对象放入三级缓存中,key是BeanName,Value是ObjectFactory

2、等到A对象属性注入时,发现依赖B,又去实例化B时

3、B属性注入需要去获取A对象,这里就是从三级缓存里拿出ObjectFactory,ObjectFactory得到对应的Bean(就是对象A)

4、把三级缓存的A记录给干掉,然后放到二级缓存中

5、显然,二级缓存存储的key是BeanName,value就是Bean(这里的Bean还没做完属性注入相关的工作)

6、等到完全初始化之后,就会把二级缓存给remove掉,塞到一级缓存中

7、我们自己去getBean的时候,实际上拿到的是一级缓存的

3.3为什么使用三级缓存

  1. 三级缓存的作用是为了解决spring中Bean依赖注入时发生的循环依赖。

  2. 如果不需要AOP,那么只需要二级缓存即可实现;

  3. 如果有AOP,其实二级缓存也能够实现,但是会打破Bean的生命周期,不符合spring的原则,

    因为需要把AOP对象放入二级缓存中(这句话好好读读!!),那么就必须在所有需要AOP处理的Bean对象初始化之前就对Bean对象进行后置处理(生成AOP对象),即使没有发生循环依赖!这并不是spring想看到的,所以spring引入了三级缓存,

  4. 而且存入的是<beanName, ObjectFactory>结构,ObjectFactory是一个lambda表达式,相当于一个回调函数,当发生循环依赖的时候,会进行lambda表达式的执行,获取到Bean对象或者 AOP代理对象,再将Bean对象或者 AOP代理对象存入二级缓存中,

  5. 如果之后还有循环依赖指向该对象(类似 A 依赖 B , B 依赖 A和C , C 依赖 A这种情况),就直接从二级缓存里面获取,从而解决了循环依赖。

  6. 这里解释了为什么不直接在二级缓存里存放lambda表达式,因为同一个lambda表达式每执行一次,就会生成一个新的代理对象,不能保证单例

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值