上一篇中,我们对容器ApplicationContext的初始化环节开始了初步的分析,我们已经找到了初始化的核心方法refresh。接下来我们看下refresh方法中做了哪些事情。
我们回到refresh方法:

可以看到,首先调用prepareRefresh方法,根据方法名我们可以知道是在为容器的初始化做一些准备工作。
我们到prepareRefresh方法里面看下:

可以看到,在prepareRefresh方法中大多数是一些初始化工作,比如设置容器的开始初始化的时间startupDate、设置容器状态closed为关闭状态、以及容器状态active设置为激活状态等。其中,initPropertySouce首先引起了我们的注意,我们进去看下

可以看到,initPropertySouce 里面却是空的,而且这个方法是通过protected修饰的。显然,这是spring为我们提供的一个扩展点,那具体是实现什么功能呢?我们从方法的注释中可以知道,是为了用实际的值来替换掉占位符。也就是说可能xml文件中,也有类似${}这样的占位符,可以通过initPropertySouce为这些占位符中的参数初始化一下属性值,方便后续解析xml文件中的信息。
我们自己写个案例测试一下:

可以看到,我们写了一个类 MyInitPropertySources 继承 ClassPathXmlApplicationContext,并且重写了方法initPropertySources。其中我们可以在系统属性中添加属性name,值为younger,这样话,如果xml文件中存在占位符“${name}”,spring 就可以拿着这个属性的值去替换占位符,简单来说方法initPropertySources存在的意义也就是这样。

接下来,我们来看下prepareRefresh方法中的下一个方法:
根据方法名称validateRequiredProperties,我们大概可以知道这个方法是用来校验是否需要某个属性的。我们可以进到方法中看下:

方法validateRequiredProperties将校验任务委托给了成员变量propertyResolver的方法validateRequiredProperties。
我们在跟到方法里面看下:


在这个方法中首先就会遍历集合requiredProperties,我们可以理解requiredProperties存放的是属性,比如环境变量,如果哪个环境变量的值不存在,就会记录到MissingRequiredPropertiesException中。当for循环结束后会再次检查,如果ex.getMissingRequiredProperties() 不为空,也就是说存在某个属性缺少对应的值就会抛出异常。根据这个特性,我们在日常的开发过程中,可以用它来判断某个环境变量是否配置,比如,我们可以用它来判定Java的环境变量是否配置了:

如果我们在方法initPropertySource中,通过方法setRequiredProperties指定必须设置环境变量名称为“JAVA_HOME”。也就是说,既然我们设置了一定需要的属性“JAVA_HOME”,那该环境变量的值一定得要存在,否则getEnvironment().validateRequiredProperties()方法就会去判断名称为“JAVA_HOME”的这个环境变量是否存在对应的值。如果不存在相应的环境变量值,就会回在我们刚才看到的地方抛出异常,很显然我们可以通过这个功能特性,可以在spring容器初始化的时候,及时检测出哪些必要的环境变量有没有配置好,及时的发现并排除隐患。

到现在为止我们记录一下当前分析流程:

这个prepareRefresh方法已经分析完了,接着我们在回到主流程的refresh 方法上:

我们再到obtainFreshBeanFactory 方法中看下:

可以看到,在obtainFreshBeanFactory方法中有两个方法,分别refreshBeanFactory和getBeanFactory。我们先到getBeanFactory方法中看一眼:


可以看到方法getBeanFactory中,其实就是直接获取beanFactory,为什么能直接获取到beanFactory呢,我们推测肯定是在getBeanFactory方法前面的refreshBeanFactory方法中就是把beanFactory这个spring初级容器给初始化完成了。我们在回到refreshBeanFactory中看下:

可以看到,首先调用方法hasBeanFactory,判断beanFactory是否存在,我们可以进去看一下:

这个就是对beanFactory进行非空判断,我们第一次没有创建beanFactory的,所以返回false。我们接着往下走

在方法createBeanFactory创建了一个DefaultListableBeanFactory类型的对象。这个DefaultListableBeanFactory我们之前没有见过,这个其实就是spring初级容器XmlBeanFactory的直接继承父类,我们先看一下DefaultListableBeanFactory这个类的继承图:

可以看到XmlBeanFactory直接继承的类就是DefaultListableBeanFactory,我们可以将这个DefaultListableBeanFactory理解为spring的初级容器。我们继续往后面看:

我们看下这个customizeBeanFactory方法,这个是来定制好我们刚才创建的beanFactory了,我们进到方法中看一下:

在方法customizeBeanFactory中,其实就是我们刚创建好的容器beanFactory设置两个属性,分别是allowBeanDefinitionOverriding 和allowCircularReferences。我们发现allowBeanDefinitionOverriding 这个参数就是之前在spring容器中注册BeanDefinition时,用来判断相同名称的BeanDefinition是否允许在容器beanDefinitionMap中被覆盖的。另外一个参数allowCircularReferences,则是后面我们讲这个bean加载的时候,用来判定是否允许多个bean之间存在循环依赖引用的。

接下来,我们在看下这个refreshBeanFactory方法中的最后一个方法:

一看到这个方法名称,我们就一个知道这就是加载我们beanDefinition的。我们到这个loadBeanDefinitions方法里面看下:

在这个方法里面,我们看到也是通过XmlBeanDefinitionReader来解析xml文件的。先创建了XmlBeanDefinitionReader对象,然后就是为beanDefinitionReader 设置环境对象、资源加载。我们还看到了它为EntityResolver 设置ResourceEntityResolver。大家还记得在ResourceEntityResolver构造方法中,为DTD和XSD这两种xml校验类型,分别指定了不同类型的解析器去jar包中获取对应的校验文件。

接下来,我们再到方法initBeanDefinitionReader 中看下:


这个方法也是被protected关键字修饰的,这就意味者方法initBeanDefinitionReader也是spring为我们提供的一个扩展点,方便子类去实现用于自定义XmlBeanDefinitionReader的。
我们接着看最后一个方法loadBeanDefinitions:

我们跟到方法中看下:

1、首先调用getConfigResources方法,可以看到getConfigResources方法默认返回null。
2、调用方法getConfigLocations获取前面封装好的xml对应的String数组。
3、通过XmlBeanDefinitionReader 调用了loadBeanDefinitions方法处理。
我们继续到loadBeanDefinitions方法里面看下:

此时依次遍历处理String数组中的每个xml路径名称,我们跟进去看一下:

可以看到,最后到重载方法loadBeanDefinition时,会发现就是根据xml文件加载对应的Resource资源了。我们继续跟进到这个重载方法看下:


这个就是回到我们之前XmlBeanFactory解析的主流程上来了。接下来就是根据这个Resource去加载对应的Document对象,然后解析Document对象,在解析xml中的各种各样的属性和标签,然后将解析到的属性和方法的信息封装到BeanDefinition中,最后在注册到spring容器beanDefinitionMap中。

我们在回到之前的refreshBeanFactory方法中看下:

最终会把beanFactory赋值给成员变量beanFactory了,所以我们在最外层可以通过getBeanFactory方法获取到它。

我们下一次再次调用refreshBeanFactory方法时,方法hasBeanFactory肯定会检测到beanFactory已经存在了,此时就会调用destroyBeans方法,将spring容器中所有已经创建好的bean全部销毁掉,并且调用closeBeanFactory方法关闭掉当前的spring容器beanFactory。

我们最后总结一下:

Spring源码解析十五相关推荐

  1. Spring源码解析(五)——自定义标签解析

    2019独角兽企业重金招聘Python工程师标准>>> 前言 作为标签解析的第二分支,也正是因为自定义标签的存在,才让Spring框架的诸多功能在短短的几行配置代码后,就生效了. 源 ...

  2. spring源码解析(五) 循环依赖

    1.什么是循环依赖? Bean A → Bean B → Bean A 在A对象生命周期中注入B,进入B生命周期找A,但A还不存在,继续进入A对象生命周期找B,这就是循环依赖. 2.循环依赖造成的结果 ...

  3. Spring源码解析十

    上一篇中,我们已经找到了默认标签解析入口,也就是方法parseDefaultElement,下面我们接着从这个方法分析: 我们到方法parseDefaultElement中,看下Spring是如何解析 ...

  4. 源码解析:Spring源码解析笔记(五)接口设计总览

    本文由colodoo(纸伞)整理 QQ 425343603 Java学习交流群(717726984) Spring解析笔记 启动过程部分已经完成,对启动过程源码有兴趣的朋友可以作为参考文章. 源码解析 ...

  5. Spring源码解析:自定义标签的解析过程

    2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...

  6. Spring源码解析【完整版】--【bilibili地址:https://www.bilibili.com/video/BV1oW41167AV】

    [本文为bilibili视频雷丰阳的Spring源码解析的完整版总结文章,其中文章前面大部分为他人博文的搬运,后面补充了其未总结的部分] 一.Java的注解 1. 注解的概念 注释:用文字描述程序,给 ...

  7. Spring 源码解析 - Bean创建过程 以及 解决循环依赖

    一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...

  8. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...

  9. ElasticSearch源码解析(五):排序(评分公式)

    ElasticSearch源码解析(五):排序(评分公式) 转载自:http://blog.csdn.net/molong1208/article/details/50623948   一.目的 一个 ...

最新文章

  1. 如何看待「上帝掷骰子」这场概率骗局
  2. 《Essential ASP.NET 2.0中文版》
  3. python实现api server_使用Python的http.server实现一个简易的Web Api对外提供HanLP拼音转换服务...
  4. 麦克纳姆轮运动原理_麦克纳姆轮介绍
  5. “被狗啃”的按钮引发的开源社区信任危机
  6. android模拟器默认位置的修改
  7. redhat 7 oracle 11,redhat7 搭建oracle 11g RAC 问题与处理
  8. 使用yarn运行react项目指令_Jenkins | 使用yarn构建前端项目
  9. 产品id 关联 分类id mysql_MySQL的多表联查
  10. project安装包_Project项目计划进度可编辑模板及安装包免费获取
  11. 揭穿内存厂家“谎言”,实测内存带宽真实表现
  12. 爬取大麦网演出信息保存为CSV文件并制作词云
  13. 伪相关、伪关系与中介变量——统计名词中的迷思
  14. OpenCV—python 图像相似度算法(dHash,方差)
  15. R语言——如何调用自己写的函数
  16. Python 小项目 猜数字小游戏
  17. java全拼_Java获取汉字对应的拼音(全拼或首字母)
  18. 改变程序黑窗口的背景和字体颜色
  19. 使用斯凯平台图片缩放函数注意的地方
  20. Apollo与ROS

热门文章

  1. HTML中详述jQuery事件绑定方式
  2. 集合添加元素python_Python 集合(set)添加元素-Python 集合(set) add-Python 集合(set) update-嗨客网...
  3. PCB上电源走线注意
  4. 华为云 两个手机 同步_HDC.Cloud | 技术探秘:华为云鲲鹏云手机何以公有云业界独家...
  5. Docker报错Error spawning command line “dbus-launch --autolaunch=xxx --binary-syntax --close-stderr”
  6. 炫“库”行动-人大金仓有奖征文-数据库的备份及恢复
  7. QQ2013 协议分析
  8. 前端工作过程遇到的问题总结(九)
  9. 靶场练习--春秋云境-Certify
  10. servlet cannot be resolved to a type的原因及解决方法