100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Spring使用三级缓存解决循环依赖?终于完全弄明白了

Spring使用三级缓存解决循环依赖?终于完全弄明白了

时间:2018-09-23 06:48:18

相关推荐

Spring使用三级缓存解决循环依赖?终于完全弄明白了

文章阅读前推荐

推荐先去看看源码,源码很短,但是对于我们在脑子里构建一个完整思路很重要。看起来非常简单,只需要双击shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到550行左右的doCreateBean方法,重点看一下580行到600行这20行代码就行,包含了三级缓存、属性注入、初始化,精华都在这20行,实在没条件的可以直接看文末附带的doCreateBean方法源码

Spring可以自动解决的循环依赖

public class AService {@Autowiredprivate BService bService;}public class BService {@Autowiredprivate AService aService;}

Spring无法自动解决构造器的循环依赖

public class DService {public DService(CService cService) {...}}public class CService {public CService(DService dService) {...}}

源码中关于三级缓存的定义

// 一级缓存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);

所以说,其实三级缓存就是三个Map而已

三级缓存有什么区别?

一级缓存

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

二级缓存

二级缓存中存的是半成品,没有完成属性注入和初始化,用来解决对象创建过程中的循环依赖问题

早期暴露出去的Bean,其实也就是解决循环依赖的Bean。早期的意思就是没有完完全全创建好,但是由于有循环依赖,就需要把这种Bean提前暴露出去。其实 早期暴露出去的Bean 跟 完完全全创建好的Bean 他们是同一个对象,只不过早期Bean里面的注解可能还没处理,完完全全的Bean已经处理了完了,但是他们指的还是同一个对象,只不过它们是在Bean创建过程中处于的不同状态

三级缓存

三级缓存中存的是 ObjectFactory<?> 类型的代理工厂对象,用于处理存在 AOP 时的循环依赖问题

存的是每个Bean对应的ObjectFactory对象,通过调用这个对象的getObject方法,就可以获取到早期暴露出去的Bean。

注意:这里有个很重要的细节就是三级缓存只会对单例的Bean生效,像多例的是无法利用到三级缓存的,通过三级缓存所在的类名DefaultSingletonBeanRegistry就可以看出,仅仅是对SingletonBean也就是单例Bean有效果。

发生循环依赖时的执行流程(精华必读)

正常不存在循环依赖的A、B对象是依次创建的,但是如果存在循环依赖的话,创建A的过程中,会顺便把B也创建了。注意,每次获取bean对象都会先去一级缓存看有没有值。

具体流程是:

1、遍历待创建的所有beanName,第一次遍历,开始获取A,此时缓存中没有,会开始正常创建流程

2、A初始创建完成,然后判断A是否是单例,且没有创建完毕,如果是,那么就会把A的beanFactory存入三级缓存

3、A开始处理@Autowired注解,开始注入B属性,于是尝试从缓存获取B,获取不到,则开始正常创建B的流程

4、B初始创建完成,同样判断B是否是单例,且没有创建完毕,如果是,那么就会把B的beanFactory存入三级缓存

5、B开始处理@Autowired注解,开始注入A属性,于是依次从一级缓存、二级缓存查找A属性,都没有就尝试从三级缓存获取A的beanFactory,通过beanFactory.getObject()方法获取A属性,接下来把A存入二级缓存,清除三级缓存。因为此时能获取到A,所以B的A属性能填充成功,B接着执行初始化,B处于实例化、初始化都完成的完全状态

6、B执行addSington(),把完全状态的B存入一级缓存,清空二三级缓存(实际只有三级有值)

7、A继续开始填充B属性,于是调用beanFactory.getBean()获取B,第六步已经把B存入一级缓存,此时直接返回,填充成功,继续执行初始化,得到一个完全状态的A

8、A执行addSington(),把完全状态的A存入一级缓存,清空二三级缓存(实际只有二级有值)

9、第二次遍历,开始获取B,此时一级缓存中有B,直接返回。

至此A、B全部实例化、初始化完成

疑惑解答

问题一:步骤5中,为什么要把B放入二级缓存?

答:主要是怕还有其他的循环依赖,如果还有的话,直接从二级缓存中就能拿到早期的AService对象

问题二:步骤6中,为什么要清空二三级缓存?

答:因为后续其他bean中也需要注入B时,会按顺序从一级缓存直到三级缓存查找,一级缓存有了,二三级缓存中的就不需要了,节省空间

问题三:文章开头提到,构造器造成的循环依赖三级缓存解决不了,为什么?

答:因为构造器循环依赖是发生在bean实例化阶段,此时连早期对象都还没创建出来,拿什么放到三级缓存。三级缓存只能是在bean实例化之后,才能起到作用

问题四:不用三级缓存,只用二级缓存能不能解决循环依赖?

答:不能,因为通过ObjectFactory获取的Bean可能是两种类型,第一种就是实例化阶段创建出来的对象,还是一种就是实例化阶段创建出来的对象的代理对象。至于是不是代理对象,取决于你的配置,如果添加了事务注解又或是自定义AOP切面,那就需要代理。假设舍弃第三级缓存,也就是没有ObjectFactory,那么就需要往第二缓存放入早期的Bean,那么是放没有代理的Bean还是被代理的Bean呢,这是在后面的属性注入阶段,处理注解的时候才能分辨的?

1)如果直接往二级缓存添加没有被代理的Bean,那么可能注入给其它对象的Bean跟最后最后完全生成的Bean是不一样的,因为最后生成的是代理对象,这肯定是不允许的;

2)那么如果直接往二级缓存添加一个代理Bean呢?

● 假设没有循环依赖,提前暴露了代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错,

● 假设没有循环依赖,使用了ObjectFactory,那么就不会提前暴露了代理对象,到最后生成的对象是什么就是什么,就不会报错,

● 如果有循环依赖,不论怎样都会提前暴露代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错

通过上面分析,如果没有循环依赖,使用ObjectFactory,就减少了提前暴露代理对象的可能性,从而减少报错的可能。

问题五:如果把二级缓存去掉,只留下一级、三级缓存呢?

答:假设舍弃第二级缓存,也就是没有存放早期的Bean的缓存,其实肯定也不行。上面说过,ObjectFactory其实获取的对象可能是代理的对象,那么如果每次都通过ObjectFactory获取代理对象,那么每次都重新创建一个代理对象,这肯定也是不允许的。

doCreateBean()方法源码(带注释)

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);}//1、通过BeanDefinition实例化对象if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// 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");}//将对象的工厂加入到三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.Object exposedObject = bean;try {//属性注入(在这里解析@Autowired注解时,触发循环依赖)populateBean(beanName, mbd, instanceWrapper);//初始化exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {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.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}

从缓存中获取Bean的源码

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 从一级缓存中获取Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 如果一级缓存里没有 且 bean正在创建中synchronized (this.singletonObjects) {// 从二级缓存里获取singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 二级缓存没有 从三级缓存获取一个工厂ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 能获取到工厂 则创建beansingletonObject = singletonFactory.getObject();// 把实例存入二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 把工厂从三级缓存移除this.singletonFactories.remove(beanName);}}}}return singletonObject;}

–我是“三七有脾气”,一个在互联网"苟且偷生"的Java程序员

“如果感觉博客对你有用,麻烦给个点赞、评论、收藏,谢谢”

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。