做者:小傅哥
博客:https://bugstack.cnhtml
沉淀、分享、成长,让本身和他人都能有所收获!😄
延迟知足能给你带来什么?
java
大学有四年时间,但几乎全部人都是临近毕业才发现找一份好工做费劲,尤为是我能很是熟悉的软件开发行业,即便是毕业了还须要额外花钱到培训机构,在学一遍编程技术才能出去找工做。好像在校这几年压根就没学到什么!git
就我我的而言多是由于上学期间喜欢编程,也从师哥、师姐那里听到一些关于毕业后找工做的不容易,也了解了一些社会上对程序员开发技能的要求级别。也就是获得了这些消息,又加上本身乐于折腾,我给本身定了一个天天都能完成的小目标:程序员
红尘世界几个王,我自不服迎头上。 日敲代码两百行,冲进世界五百强。
哈哈哈,就这么天天两百行代码,一个月就是6千行,一年就是6万行,三年后开始实习就有18万行,一个应届实习生有将近20万行代码的敲击量,几乎已经能够很是熟练的完成各种简单的工做,在加上实习中对整个项目流程真正的断链后,找一个正经
的开发工做,仍是很容易的。github
而这时候找工做的容易,就来自于你一直以来的学习和沉淀,但若是你没通过这些努力,可能等毕业后就会变得很是慌乱,最后没办法只能去一些机构再学习一遍。面试
谢飞机,小记!
,之前感受Spring没啥,看过一篇getBean,个人天!spring
谢飞机:面试官,最近我看了 Spring 的 getBean 发现这里好多东西,还有一个是要解决循环依赖的,这玩意面试有啥要问的吗?编程
面试官:有哇,Spring 是如何解决循环依赖的?设计模式
谢飞机:嗯,经过三级缓存提早暴露对象解决的。缓存
面试官:能够哈,那这三个缓存里都存放了什么样的对象信息呢?
谢飞机:一级缓存存放的是完整对象,也叫成品对象。二级缓存存放的是半成品对象,就是那些属性还没赋值的对象。三级缓存存放的是 ObjectFactory<?>
类型的 lambda 表达式,就是这用于处理 AOP 循环依赖的。
面试官:能够呀,谢飞机有所准备嘛!那若是没有三级缓存,只有二级或者一级,能解决循环依赖吗?
谢飞机:其实我看过资料了,能够解决,只不过 Spring 要保证几个事情,只有一级缓存处理流程无法拆分,复杂度也会增长,同时半成品对象可能会有空指针异常。而将半成品与成品对象分开,处理起来也更加优雅、简单、易扩展。另外 Spring 的两大特性中不只有 IOC 还有 AOP,也就是基于字节码加强后的方法,该存放到哪,而三级缓存最主要,要解决的循环依赖就是对 AOP 的处理,但若是把 AOP 代理对象的建立提早,那么二级缓存也同样能够解决。可是,这就违背了 Spring 建立对象的原则,Spring 更喜欢把全部的普通 Bean 都初始化完成,在处理代理对象的初始化。
面试官:飞机,不错嘛,此次了解了很多。那问个简单的,你撸过循环依赖的解决方案?
谢飞机:哦哦,这没有,没实践过!!!确实应该搞一下,试试。
了解问题的本质再分析问题,每每更利于对问题有更深刻的了解和研究。因此咱们在分析 Spring 关于循环依赖的源码以前,先要了解下什么是循环依赖。
public class ABTest { public static void main(String[] args) { new ClazzA(); } } class ClazzA { private ClazzB b = new ClazzB(); } class ClazzB { private ClazzA a = new ClazzA(); }
java.lang.StackOverflowError
在这部分的代码中就一个核心目的,咱们来本身解决一下循环依赖,方案以下:
public class CircleTest { private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); public static void main(String[] args) throws Exception { System.out.println(getBean(B.class).getA()); System.out.println(getBean(A.class).getB()); } private static <T> T getBean(Class<T> beanClass) throws Exception { String beanName = beanClass.getSimpleName().toLowerCase(); if (singletonObjects.containsKey(beanName)) { return (T) singletonObjects.get(beanName); } // 实例化对象入缓存 Object obj = beanClass.newInstance(); singletonObjects.put(beanName, obj); // 属性填充补全对象 Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Class<?> fieldClass = field.getType(); String fieldBeanName = fieldClass.getSimpleName().toLowerCase(); field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass)); field.setAccessible(false); } return (T) obj; } } class A { private B b; // ...get/set } class B { private A a; // ...get/set }
getBean
,是整个解决循环依赖的核心内容,A 建立后填充属性时依赖 B,那么就去建立 B,在建立 B 开始填充时发现依赖于 A,但此时 A 这个半成品对象已经存放在缓存到singletonObjects
中了,因此 B 能够正常建立,在经过递归把 A 也建立完整了。
经过上面的例子咱们大概了解到,A和B互相依赖时,A建立完后填充属性B,继续建立B,再填充属性A时就能够从缓存中获取了,以下:
那这个解决事循环依赖的事放到 Spring 中是什么样呢?展开细节!
虽然,解决循环依赖的核心原理同样,但要放到支撑起整个 Spring 中 IOC、AOP 特性时,就会变得复杂一些,整个处理 Spring 循环依赖的过程以下;
说说细节
关于本章节涉及到的案例源码分析,已更新到 github:https://github.com/fuzhengwei/interview - interview-31
如下是单元测试中对AB依赖的获取Bean操做,重点在于进入 getBean 的源码跟进;
@Test public void test_alias() { BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml"); Bean_A bean_a = beanFactory.getBean("bean_a", Bean_A.class); logger.info("获取 Bean 经过别名:{}", bean_a.getBean_b()); }
org.springframework.beans.factory.support.AbstractBeanFactory.java
@Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException { return doGetBean(name, requiredType, null, false); }
doGetBean 方法
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { // 从缓存中获取 bean 实例 Object sharedInstance = getSingleton(beanName); // mbd.isSingleton() 用于判断 bean 是不是单例模式 if (mbd.isSingleton()) { // 获取 bean 实例 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { // 建立 bean 实例,createBean 返回的 bean 实例化好的 return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); // 后续的处理操做 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... // 返回 bean 实例 return (T) bean; }
doCreateBean 方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // 建立 bean 实例,并将 bean 实例包装到 BeanWrapper 对象中返回 instanceWrapper = createBeanInstance(beanName, mbd, args); // 添加 bean 工厂对象到 singletonFactories 缓存中 addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { // 获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回 bean。 return getEarlyBeanReference(beanName, mbd, bean); } }); try { // 填充属性,解析依赖关系 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } // 返回 bean 实例 return exposedObject; }
createBeanInstance
,建立 bean 实例,并将 bean 实例包装到 BeanWrapper 对象中返回addSingletonFactory
,添加 bean 工厂对象到 singletonFactories 缓存中getEarlyBeanReference
,获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回 bean。populateBean
,填充属性,解析依赖关系。也就是从这开始去找寻 A 实例中属性 B,紧接着去建立 B 实例,最后在返回回来。getSingleton 三级缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从 singletonObjects 获取实例,singletonObjects 是成品 bean Object singletonObject = this.singletonObjects.get(beanName); // 判断 beanName ,isSingletonCurrentlyInCreation 对应的 bean 是否正在建立中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 从 earlySingletonObjects 中获取提早曝光未成品的 bean singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 获取相应的 bean 工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 提早曝光 bean 实例,主要用于解决AOP循环依赖 singletonObject = singletonFactory.getObject(); // 将 singletonObject 放入缓存中,并将 singletonFactory 从缓存中移除 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
singletonObjects.get(beanName)
,从 singletonObjects 获取实例,singletonObjects 是成品 beanisSingletonCurrentlyInCreation
,判断 beanName ,isSingletonCurrentlyInCreation 对应的 bean 是否正在建立中allowEarlyReference
,从 earlySingletonObjects 中获取提早曝光未成品的 beansingletonFactory.getObject()
,提早曝光 bean 实例,主要用于解决AOP循环依赖综上,是一个处理循环依赖的代码流程,这部分提取出来的内容主要为核心内容,并没与长篇大论的所有拆取出来,你们在调试的时候会涉及的比较多,尽量要本身根据流程图操做调试几遍。
综上从咱们本身去尝试解决循环依赖,学习了循环依赖的核心解决原理。又分析了 Spring 解决的循环依赖的处理过程以及核心源码的分析。那么接下来咱们在总结下三级缓存分别不一样的处理过程,算是一个总结,也方便你们理解。
ObjectFactory<?>
类型的 lambda 表达式,而 Spring 的原则又不但愿将此类类型的 Bean 前置建立,因此要存放到三级缓存中处理。