Spring IOC源码(二):IOC容器之 刷新前的准备

1、源码解析

  prepareRefresh()容器刷新refresh()的第一个方法,是容器刷新前的准备工作。
复制代码
 1 // 容器启动的开始时间  毫秒级
 2 private long startupDate;
 3 // 容器目前是否活跃的标记
 4 private final AtomicBoolean active = new AtomicBoolean();
 5 // 当前容器是否已经被关闭的标记
 6 private final AtomicBoolean closed = new AtomicBoolean();
 7 // 容器刷新前注册进来的监听器集合
 8 private Set<ApplicationListener<?>> earlyApplicationListeners;
 9 // 容器的监听器集合
10 private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
11 // 要发布的应用程序事件集合(在多播器未设置之前要发布的事件)
12 private Set<ApplicationEvent> earlyApplicationEvents;
13 
14 // 刷新容器前的准备工作
15 protected void prepareRefresh() {
16    // Switch to active.
17    // 记录容器启动时间
18    this.startupDate = System.currentTimeMillis();
19    // 设置关闭状态为false
20    this.closed.set(false);
21    // 设置活跃状态为true
22    this.active.set(true);
23 
24    // 日志记录
25    if (logger.isDebugEnabled()) {
26       if (logger.isTraceEnabled()) {
27          logger.trace("Refreshing " + this);
28       }
29       else {
30          logger.debug("Refreshing " + getDisplayName());
31       }
32    }
33 
34    // 具体实现由子类完成,初始化属性资源
35    initPropertySources();
36 
37    // 获取Environment对象,并验证当前系统需要的属性值是否都加载到了Environment对象中
38    getEnvironment().validateRequiredProperties();
39 
40    // 准备监听器和事件的集合对象,默认为空的集合
41    if (this.earlyApplicationListeners == null) {
42       this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
43    }
44    else {
45       // 重置本地应用监听器为预刷新状态,清空集合缓存
46       this.applicationListeners.clear();
47       // 将容器刷新前的监听器与事件集合对象设置到当前容器监听器集合中
48       this.applicationListeners.addAll(this.earlyApplicationListeners);
49    }
50    
51    // 创建刷新前的事件集合,依赖于多播器,一旦多播器是可用的,事件集合中的事件就可以被发布
52    this.earlyApplicationEvents = new LinkedHashSet<>();
53 }
复制代码

综上可见,容器刷新前的准备工作prepareRefresh()完成以下事情:

  1、记录容器启动时间

  2、设置当前容器关闭状态为false
  3、设置当前容器活跃状态为true
  4、设置拓展点,初始化资源占位符
  5、设置环境Environment对象,并将当前系统属性值加载至环境对象中
  6、监听器、事件集合的处理 - Spring框架提供的扩展点,在SpringBoot中有应用。

2、应用

  Spring在prepareRefresh()有对初始化资源占位符initPropertySources()的拓展点,下面我们来看看如何实现拓展。
  initPropertySources()方法是AbstractApplicationContext中的待子类实现的方法。
protected void initPropertySources() {
   // For subclasses: do nothing by default.
}

  从 IOC源码(一):IOC容器启动流程核心方法概览中ClassPatchXmlApplicationContext类图中我们得知,ClassPatchXmlApplicationContext是AbstractApplicationContext的子类,下面我们来看看初始化资源占位符的具体实现。

2.1、定义配置文件 - snailsInitPropertySource.xml

复制代码
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3       xmlns:context="http://www.springframework.org/schema/context"
4       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
6        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">
7 
8 </beans>
复制代码

2.2、拓展初始化属性资源,重写initPropertySources()方法

复制代码
 1 import org.springframework.beans.BeansException;
 2 import org.springframework.context.support.ClassPathXmlApplicationContext;
 3 
 4 // 初始化属性资源的拓展实现
 5 public class SnailsInitApplicationContext extends ClassPathXmlApplicationContext {
 6   
 7    public SnailsInitApplicationContext(String... configLocations) throws BeansException {
 8       super(configLocations);
 9    }
10 
11    @Override
12    protected void initPropertySources() {
13       System.out.println("expand initPropertySource... ");
14       // 添加属性到Environment环境中
15       getEnvironment().getSystemProperties().put("address","中国");
16       // Environment环境中的必输属性,会通过validateRequiredProperties方法做校验
17        getEnvironment().setRequiredProperties("addr");
18    }
19 }
复制代码

 2.3、测试代码

复制代码
 1 /**
 2  * @Description: 测试拓展初始化资源属性拓展
 3  * @author: snails
 4  * @since: 2022/3/1 11:02
 5  */
 6 public class TestSnailsInitApplicationContext {
 7    public static void main(String[] args) {
 8       SnailsInitApplicationContext context = new SnailsInitApplicationContext("snailsInitPropertySource.xml");
 9    }
10 }
复制代码

2.4、执行结果

  getEnvironment().getSystemProperties().put("address","中国");
0
  address属性已经加载到environment#propertySources#propertySourceList集合中name为systemProperties的 source集合中,source中存储的是Properties对象。
  getEnvironment().setRequiredProperties("addr");
复制代码
Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [addr]
at org.springframework.core.env.AbstractPropertyResolver.validateRequiredProperties(AbstractPropertyResolver.java:145)
at org.springframework.core.env.AbstractEnvironment.validateRequiredProperties(AbstractEnvironment.java:519)
at org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:646)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:153)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:97)
at selfInitPropertySources.SnailsInitApplicationContext.<init>(SnailsInitApplicationContext.java:14)
at selfInitPropertySources.TestSnailsInitApplicationContext.main(TestSnailsInitApplicationContext.java:11)
复制代码

校验失败,抛异常,校验流程分析如下

  1、将要校验的属性addr加入校验集合requiredProperties中;
  2、AbstractPropertyResolver#validateRequiredProperties()校验,遍历校验集合requiredProperties,通过key=addr从environment中的source集合中获取value;
  3、若通过key获取的value为空,抛出异常。
posted @ 2022-12-10 19:15  无虑的小猪  阅读(103)  评论(0编辑  收藏  举报