IoC容器体系
IoC容器是Spring的核⼼模块,是抽象了对象管理、依赖关系管理的框架解决⽅案。<br>Spring IoC 容器继承体系需要使⽤哪个层次⽤哪个层次即可。不是把所有接口都放在BeanFactory<br>ApplicationContext 还继承了ResourceLoader、MessageSource<br>
Bean⽣命周期关键时机点
详细见代码。创建⼀个类 Bean ,让其实现⼏个特殊的接⼝,并分别在接⼝实现的构造器、接⼝⽅法中<br>断点,观察线程调⽤栈,分析出 Bean 对象创建和管理关键点的触发时机。
根据上⾯的调试分析,我们发现 Bean对象创建的⼏个关键时机点代码层级的调⽤都在<br>AbstractApplicationContext 类 的 refresh ⽅法中,可⻅这个⽅法对于Spring IoC 容器初始化来说相当<br>关键,汇总如下:<br>构造器 - refresh#finishBeanFactoryInitialization(beanFactory)(beanFactory)<br>BeanFactoryPostProcessor 初始化refresh#invokeBeanFactoryPostProcessors(beanFactory)<br>BeanFactoryPostProcessor ⽅法调⽤refresh#invokeBeanFactoryPostProcessors(beanFactory)<br>BeanPostProcessor 初始化registerBeanPostProcessors(beanFactory)<br>BeanPostProcessor ⽅法调⽤refresh#finishBeanFactoryInitialization(beanFactory)<br>
子主题
IoC容器初始化主流程(refresh)
第⼀步:刷新前的预处理 prepareRefresh();
第⼆步:获取BeanFactory;默认实现是DefaultListableBeanFactory<br>加载BeanDefition 并注册到 BeanDefitionRegistry<br>ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();<br>
第三步:BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加<br>载器等)<br>prepareBeanFactory(beanFactory);
第四步:BeanFactory准备⼯作完成后进⾏的后置处理⼯作<br>postProcessBeanFactory(beanFactory);
第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean<br>invokeBeanFactoryPostProcessors(beanFactory);
第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执<br>⾏<br>registerBeanPostProcessors(beanFactory);
第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);<br>initMessageSource();
第⼋步:初始化事件派发器<br>initApplicationEventMulticaster();
第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑<br>onRefresh();
第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean<br>registerListeners();
第⼗⼀步:<br>初始化所有剩下的⾮懒加载的单例bean<br>初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)<br>填充属性<br>初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)<br>调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处<br>finishBeanFactoryInitialization(beanFactory);<br>
第⼗⼆步:<br>完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事<br>件 (ContextRefreshedEvent)<br>finishRefresh();<br>
BeanFactory创建流程
获取BeanFactory⼦流程
BeanDefinition加载解析及注册⼦流程
Resource定位:指对BeanDefinition的资源定位过程。通俗讲就是找到定义Javabean信息的XML⽂<br>件,并将其封装成Resource对象。
子主题
BeanDefinition载⼊ :把⽤户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数<br>据结构就是BeanDefinition。
Bean创建流程
finishBeanFactoryInitialization,创建子流程入口<br>beanFactory.preInstantiateSingletons() 实例化所有,立即加载单例bean<br>最后跟到AbstractBeanFactory类的doGetBean⽅法<br>最后调用 initializeBean
lazy-init 延迟加载机制原理
lazy-init 延迟加载机制分析
<ol><li>Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap⾥供下⾯的初始化时⽤,然后对每个BeanDefinition 进⾏处理。</li><li>如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进⾏初始化并依赖注⼊。最后通过外部getBean调用。<br>preInstantiateSingletons() 下 !bd.isLazyInit() 判断懒加载,是否需要初始化。普通Bean 直接调用getBean<br></li></ol>
总结
<ol><li>对于被修饰为lazy-init的bean Spring 容器初始化阶段不会进⾏ init 并且依赖注⼊,当第⼀次<br>进⾏getBean时候才进⾏初始化并依赖注⼊</li><li>对于⾮懒加载的bean,getBean的时候会从缓存⾥头获取,因为容器初始化阶段 Bean 已经<br>初始化完成并缓存了起来</li></ol>
Spring IoC循环依赖问题
什么是循环依赖
解释:循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A<br>依赖于B,B依赖于C,C⼜依赖于A。
Spring中循环依赖场景有<br>1.构造器的循环依赖(构造器注⼊) <br>2.Field 属性的循环依赖(set注⼊)<br>构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决<br>属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。<br>
循环依赖处理机制
<span style="font-size: inherit;">分析:<br></span><br style="font-size: inherit;"><span style="font-size: inherit;">单例 bean 构造器参数循环依赖(⽆法解决)</span><br><span style="font-size: inherit;">prototype 原型 bean循环依赖(⽆法解决)</span><br><span style="font-size: inherit;">对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx⽅法产⽣循环依赖,Spring都 会直接报错处理。</span><br><span style="font-size: inherit;">Spring 的循环依赖的理论依据基于 Java 的引⽤传递,当获得对象的引⽤时,对象的属性是可以延后设置的,但是构造器必须是在获取引⽤之前</span><br>
解决流程:(涉及到使用三级缓存)二级缓存的作用-> 升级过程中可以完成一些扩展操作<br><br><ol><li>Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。(将A放入三级缓存中,暴露自己)</li><li>ClassA调⽤setClassB⽅法,Spring⾸先尝试从容器中获取ClassB,此时ClassB不存在Spring容器中。(从三级缓存中取A)</li><li>Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中(将A提到二级缓存中)</li><li>ClassB调⽤setClassA⽅法,Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了ClassA,因此可以获取到ClassA实例</li><li>ClassA通过spring容器获取到ClassB,完成了对象初始化操作。(完成后将A提到二级缓存中)</li><li>这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。(B创建完放入一级缓存,A直接从一级缓存取)</li></ol>