定焦原则︰抓主线
宏观原则:站在上帝视角,关注源码结构和业务流程(淡化具体某行代码的编写细节)
断点(观察调用栈)
反调(Find Usages)
经验(spring框架中doXXX,做具体处理的地方)
下载源码(github)
安装gradle 5.6.3(类似于maven) ldea 2019.1 Jdk 11.0.5
导入(耗费—定时间)
编译工程(顺序: core-oxm-context-beans-aspects-aop)
工程—>tasks->compileTestJava
1.1 Spring loC的容器体系
loC容器是Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案。Spring提供了很多的容器,其中BeanFactory是顶层容器(根容器),不能被实例化,它定义了所有loC容器必须遵从的一套原则,具体的容器实现可以增加额外的功能,比如我们常用到的ApplicationContext,其下更具体的实现如ClassPathXmlApplicationContext包含了解析xml等一系列的内容,AnnotationConfigApplicationContext则是包含了注解解析等一系列的内容。Spring loC容器继承体系非常聪明,需要使用哪个层次用哪个层次即可,不必使用功能大而全的。BeanFactory ]顶级接口方法栈如下
通过其接口设计,我们可以看到我们一贯使用的ApplicationContext除了继承BeanFactory的子接口,还继承了ResourceLoader、MessageSource等接口,因此其提供的功能也就更丰富了。
下面我们以ClasspathXmIApplicationContext为例,深入源码说明loC容器的初始化流程。
1.2Bean生命周期关键时机点
思路︰创建一个类LagouBean,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出Bean对象创建和管理关键点的触发时机。
LagouBean类
package com.lagou;import org.springframework.beans.BeansException;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.BeanPostProcessor;importorg.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.stereotype.Component;/*** @Author 斗帝* @create 2019/12/3 11:46*/public class LagouBean implements InitializingBean{ /** * 构造函数 */ public LagouBean(){ System.out.println("LagouBean 构造器..."); } /** * InitializingBean 接口实现 */ public void afterPropertiesSet() throws Exception { System.out.println("LagouBean afterPropertiesSet..."); } }
BeanPostProcessor 接口实现类
package com.lagou;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.stereotype.Component;/*** @Author 斗帝* @create 2019/12/3 16:59*/public class MyBeanPostProcessor implements BeanPostProcessor { public MyBeanPostProcessor() { System.out.println("BeanPostProcessor 实现类构造函数..."); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException { if("lagouBean".equals(beanName)) { System.out.println("BeanPostProcessor 实现类postProcessBeforeInitialization 方法被调用中......"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException { if("lagouBean".equals(beanName)) { System.out.println("BeanPostProcessor 实现类postProcessAfterInitialization 方法被调用中......"); } return bean; } }
BeanFactoryPostProcessor 接口实现类
package com.lagou;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;importorg.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.stereotype.Component;/*** @Author 斗帝* @create 2019/12/3 16:56*/public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { public MyBeanFactoryPostProcessor() { System.out.println("BeanFactoryPostProcessor的实现类构造函数..."); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory) throws BeansException { System.out.println("BeanFactoryPostProcessor的实现方法调用中......"); } }
applicationContext.xml
IoC 容器源码分析用例
/*** Ioc 容器源码分析基础案例*/@Testpublic void testIoC() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); LagouBean lagouBean = applicationContext.getBean(LagouBean.class); System.out.println(lagouBean);}
(1)分析 Bean 的创建是在容器初始化时还是在 getBean
根据断点调试,我们发现,在未设置延迟加载的前提下,Bean 的创建是在容器初始化过程中完成的。
(2)分析构造函数调用情况
通过如上观察,我们发现构造函数的调用时机在AbstractApplicationContext类refresh方法的finishBeanFactorylnitialization(beanFactory)处;
(3)分析InitializingBean之afterPropertiesSet初始化方法调用情况
观察调用栈
通过如上观察,我们发现InitializingBean中afterPropertiesSet方法的调用时机也是在AbstractApplicationContext类refresh方法的finishBeanFactoryInitialization(beanFactory);
(4)分析BeanFactoryPostProcessor初始化和调用情况
分别在构造函数、postProcessBeanFactory方法处打断点,观察调用栈,发现BeanFactoryPostProcessor初始化在AbstractApplicationContext类refresh方法的invokeBeanFactoryPostProcessors(beanFactory);
postProcessBeanFactory 调用在AbstractApplicationContext类refresh方法的invokeBeanFactoryPostProcessors(beanFactory);
(5)分析BeanPostProcessor初始化和调用情况
分别在构造函数、postProcessBeanFactory方法处打断点,观察调用栈,发现BeanPostProcessor初始化在AbstractApplicationContext类refresh方法的registerBeanPostProcessors(beanFactory);
postProcessBeforelnitialization 调用在AbstractApplicationContext类refresh方法的finishBeanFactorylnitialization(beanFactory);
postProcessAfterInitialization调用在AbstractApplicationContext类refresh方法的finishBeanFactoryInitialization(beanFactory);
(6)总结
根据上面的调试分析,我们发现Bean对象创建的几个关键时机点代码层级的调用都在AbstractApplicationContext类的refresh方法中,可见这个方法对于Spring loC容器初始化来说相当关键,汇总如下:
1.3 Spring loC容器初始化主流程
由上分析可知,Spring loC容器初始化的关键环节就在AbstractApplicationContext#refresh()方法中,我们查看refresh方法来俯瞰容器创建的主体流程,主体流程下的具体子流程我们后面再来讨论。
@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 第一步:刷新前的预处理 prepareRefresh(); /* 第二步: 获取BeanFactory;默认实现是DefaultListableBeanFactory 加载BeanDefition 并注册到 BeanDefitionRegistry */ ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory(); // 第三步:BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加载器等) prepareBeanFactory(beanFactory); try { // 第四步:BeanFactory准备⼯作完成后进⾏的后置处理⼯作 postProcessBeanFactory(beanFactory); // 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean invokeBeanFactoryPostProcessors(beanFactory); // 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏ registerBeanPostProcessors(beanFactory); // 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析); initMessageSource(); // 第八步:初始化事件派发器 initApplicationEventMulticaster(); // 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑 onRefresh(); // 第十步:注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean registerListeners(); /* 第十一步: 初始化所有剩下的非懒加载的单例bean 初始化创建非懒加载方式的单例Bean实例(未设置属性) 填充属性 初始化方法调用(比如调用afterPropertiesSet方法、init-method方法) 调⽤BeanPostProcessor(后置处理器)对实例bean进行后置处 */ finishBeanFactoryInitialization(beanFactory); /* 第十二步: 完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()方法,并且发布事件 (ContextRefreshedEvent) */ finishRefresh(); } ...... }}
2.1获取BeanFactory子流程
时序图如下
2.2 BeanDefinition加载解析及注册子流程
(⑴)该子流程涉及到如下几个关键步骤
Resource定位∶指对BeanDefinition的资源定位过程。通俗讲就是找到定义avabean信息的XML文件,并将其封装成Resource对象。
BeanDefinition载入︰把用户定义好的Javabean表示为loC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。
注册BeanDefinition到loC容器
(2)过程分析
Step1:子流程入口在AbstractRefreshableApplicationContext#refreshBeanFactory方法中
Step2∶依次调用多个类的loadBeanDefinitions方法一>AbstractXmlApplicationContext一>AbstractBeanDefinitionReader —> XmlBeanDefinitionReader一直执行到XmlBeanDefinitionReader的doLoadBeanDefinitions方法
Step 3:我们重点观察XmlBeanDefinitionReader类的registerBeanDefinitions方法,期间产生了多次重载调用,我们定位到最后一个
此处我们关注两个地方:一个createRederContext方法,一个是DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,先进入createRederContext方法看看
我们可以看到,此处Spring首先完成了NamespaceHandlerResolver的初始化。
我们再进入registerBeanDefinitions方法中追踪,调用了
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions方法
进入 doRegisterBeanDefifinitions 方法
进入 parseBeanDefifinitions 方法
进入 parseDefaultElement 方法
进入 processBeanDefifinition 方法
至此,注册流程结束,我们发现,所谓的注册就是把封装的XML中定义的Bean信息封装为BeanDefinition对象之后放入一个Map中,BeanFactory是以Map的结构组织这些BeanDefinition的。
可以在DefaultListableBeanFactory中看到此Map的定义
/** Map of bean definition objects, keyed by bean name. */private final Map beanDefinitionMap = newConcurrentHashMap<>(256);
(3)时序图
第3节Bean创建流程
创建Bean实例,此时尚未设置属性
给Bean填充属性,调用初始化方法,应用BeanPostProcessor后置处理器
普通Bean的初始化是在容器启动初始化阶段执行的,而被lazy-init=true修饰的bean则是在从容器里第一次进行context.getBean()时进行触发。Spring启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap里供下面的初始化时用,然后对每个BeanDefinition进行处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进行初始化并依赖注入。
1public void preInstantiateSingletons() throws BeansException { // 所有beanDefinition集合 List beanNames = new ArrayList(this.beanDefinitionNames); // 触发所有⾮懒加载单例bean的初始化 for (String beanName : beanNames) { // 获取bean 定义 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 判断是否是懒加载单例bean,如果是单例的并且不是懒加载的则在容器创建时初始化 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 判断是否是 FactoryBean if (isFactoryBean(beanName)) { final FactoryBean> factory = (FactoryBean>)getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged(newPrivilegedAction() { @Override public Boolean run() { return ((SmartFactoryBean>) factory).isEagerInit(); } }, getAccessControlContext()); } }else { /* 如果是普通bean则进行初始化并依赖注入,此 getBean(beanName)接下来触发的逻辑和 懒加载时 context.getBean("beanName") 所触发的逻辑是⼀样的 */ getBean(beanName); } } }}
总结
对于被修饰为lazy-init的bean Spring容器初始化阶段不会进行init并且依赖注入,当第一次进行getBean时候才进行初始化并依赖注入
对于非懒加载的bean,getBean的时候会从缓存里头获取,因为容器初始化阶段Bean已经初始化完成并缓存了起来
好了Spring IOC源码深度剖析就先写到这里吧,Spring源码高级笔记持续更新中,如果觉得对你有帮助的话可不可以转发+关注一波呢;
推荐阅读:
Spring源码高级笔记之——Spring概述
Spring源码高级笔记之——Spring核心思想
Spring源码高级笔记之——银行案例手写IOC和AOP
Spring源码高级笔记之——Spring IOC 应用