源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析

Spring IOC源码深度剖析

  • 好处:提高培养代码架构思维、深入理解框架
  • 原则

定焦原则︰抓主线

宏观原则:站在上帝视角,关注源码结构和业务流程(淡化具体某行代码的编写细节)

  • 读源码的方法和技巧

断点(观察调用栈)

反调(Find Usages)

经验(spring框架中doXXX,做具体处理的地方)

  • Spring源码构建

下载源码(github)

安装gradle 5.6.3(类似于maven) ldea 2019.1 Jdk 11.0.5

导入(耗费—定时间)

编译工程(顺序: core-oxm-context-beans-aspects-aop)

工程—>tasks->compileTestJava

第1节 Spring IoC容器初始化主体流程

1.1 Spring loC的容器体系

loC容器是Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案。Spring提供了很多的容器,其中BeanFactory是顶层容器(根容器),不能被实例化,它定义了所有loC容器必须遵从的一套原则,具体的容器实现可以增加额外的功能,比如我们常用到的ApplicationContext,其下更具体的实现如ClassPathXmlApplicationContext包含了解析xml等一系列的内容,AnnotationConfigApplicationContext则是包含了注解解析等一系列的内容。Spring loC容器继承体系非常聪明,需要使用哪个层次用哪个层次即可,不必使用功能大而全的。BeanFactory ]顶级接口方法栈如下

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第1张图片

BeanFactory 容器继承体系

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第2张图片

通过其接口设计,我们可以看到我们一贯使用的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

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第3张图片

根据断点调试,我们发现,在未设置延迟加载的前提下,Bean 的创建是在容器初始化过程中完成的。

(2)分析构造函数调用情况

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第4张图片
源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第5张图片

通过如上观察,我们发现构造函数的调用时机在AbstractApplicationContext类refresh方法的finishBeanFactorylnitialization(beanFactory)处;

(3)分析InitializingBean之afterPropertiesSet初始化方法调用情况

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第6张图片

观察调用栈

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第7张图片

通过如上观察,我们发现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容器初始化来说相当关键,汇总如下:

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第8张图片

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节BeanFactory创建流程

2.1获取BeanFactory子流程

时序图如下

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第9张图片

2.2 BeanDefinition加载解析及注册子流程

(⑴)该子流程涉及到如下几个关键步骤

Resource定位∶指对BeanDefinition的资源定位过程。通俗讲就是找到定义avabean信息的XML文件,并将其封装成Resource对象。

BeanDefinition载入︰把用户定义好的Javabean表示为loC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。

注册BeanDefinition到loC容器

(2)过程分析

Step1:子流程入口在AbstractRefreshableApplicationContext#refreshBeanFactory方法中

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第10张图片

Step2∶依次调用多个类的loadBeanDefinitions方法一>AbstractXmlApplicationContext一>AbstractBeanDefinitionReader —> XmlBeanDefinitionReader一直执行到XmlBeanDefinitionReader的doLoadBeanDefinitions方法

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第11张图片

Step 3:我们重点观察XmlBeanDefinitionReader类的registerBeanDefinitions方法,期间产生了多次重载调用,我们定位到最后一个

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第12张图片

此处我们关注两个地方:一个createRederContext方法,一个是DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,先进入createRederContext方法看看

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第13张图片

我们可以看到,此处Spring首先完成了NamespaceHandlerResolver的初始化。

我们再进入registerBeanDefinitions方法中追踪,调用了

DefaultBeanDefinitionDocumentReader#registerBeanDefinitions方法

3706ff451ffcea72e0f8eda2c8df792b.png

进入 doRegisterBeanDefifinitions 方法

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第14张图片

进入 parseBeanDefifinitions 方法

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第15张图片

进入 parseDefaultElement 方法

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第16张图片

进入 processBeanDefifinition 方法

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第17张图片

至此,注册流程结束,我们发现,所谓的注册就是把封装的XML中定义的Bean信息封装为BeanDefinition对象之后放入一个Map中,BeanFactory是以Map的结构组织这些BeanDefinition的。

a4fcff17c6a0834e74b1271786367762.png

可以在DefaultListableBeanFactory中看到此Map的定义

/** Map of bean definition objects, keyed by bean name. */private final Map beanDefinitionMap = newConcurrentHashMap<>(256);

(3)时序图

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第18张图片

第3节Bean创建流程

  • 通过最开始的关键时机点分析,我们知道Bean创建子流程入口在AbstractApplicationContext#refresh()方法的finishBeanFactoryInitialization(beanFactory)处
源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第19张图片
  • 进入finishBeanFactorylnitialization
源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第20张图片
  • 继续进入DefaultListableBeanFactory类的preInstantiateSingletons方法,我们找到下面部分的代码,看到工厂Bean或者普通Bean,最终都是通过getBean的方法获取实例
源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第21张图片
  • 继续跟踪下去,我们进入到了AbstractBeanFactory类的doGetBean方法,这个方法中的代码很多,我们直接找到核心部分
源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第22张图片
  • 接着进入到AbstractAutowireCapableBeanFactory类的方法,找到以下代码部分
源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第23张图片
  • 进入doCreateBean方法看看,该方法我们关注两块重点区域

创建Bean实例,此时尚未设置属性

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第24张图片

给Bean填充属性,调用初始化方法,应用BeanPostProcessor后置处理器

源码剖析_Spring源码高级笔记——Spring IOC源码深度剖析_第25张图片

第4节lazy-init延迟加载机制原理

  • lazy-init延迟加载机制分析

普通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 应用

你可能感兴趣的:(源码剖析)