Spring源码解析十五

上一篇中,我们对容器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。

我们最后总结一下:
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

youngerone123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值