【spring源码系列-05】refresh中prepareRefresh方法的执行流程

简介: 【spring源码系列-05】refresh中prepareRefresh方法的执行流程

一,深度剖析refresh的prepareRefresh方法

前两篇谈到了refresh方法的前置工作和准备工作有哪些,注解的方式相对而言会比xml的方式需要做的前置工作更多。接下来就是进入最主要的refresh部分,前面几篇也粗略的对refresh里面的12个方法进行了粗略的概括,接下来的文章中,将对每一个方法做一个详细的概括。


再次查看这个refresh方法,首先会在这个方法上面加一个同步锁 synchronized ,用来保证线程的安全性

@Override
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    //1:准备刷新上下文环境
    prepareRefresh();
    //2:获取告诉子类初始化Bean工厂,不同工厂有不同的实现
        //  并且将配置文件的属性值加载到当前工厂中
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    //3:对bean工厂进行填充属性
    prepareBeanFactory(beanFactory);
    try {
      // 第四:留个子类去实现该接口,bean工厂的后置处理器
      postProcessBeanFactory(beanFactory);
      // 调用我们的bean工厂的后置处理器. 
          //1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用
      invokeBeanFactoryPostProcessors(beanFactory);
      // 注册我们bean的后置处理器
      registerBeanPostProcessors(beanFactory);
      // 初始化国际化资源处理器.
      initMessageSource();
      // 创建事件多播器
      initApplicationEventMulticaster();
      // 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
      onRefresh();
      //把我们的事件监听器注册到多播器上
      registerListeners();
      // 实例化我们剩余的单实例bean.
      finishBeanFactoryInitialization(beanFactory);
      // 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
      finishRefresh();
    }
    }
}

1,prepareRefresh()具体的执行流程

接下来就是refresh中的第一个方法,也是本文的重点:prepareRefresh(),顾名思义,就是刷新前的准备工作,接下来进入这个方法中,查看内部详细的执行流程

9ece3a34dfbe4a758e05d58b7ec90f8b.png



其主要代码片段如下

protected void prepareRefresh() {
  // Switch to active.
  this.startupDate = System.currentTimeMillis();
  this.closed.set(false);
  this.active.set(true);
  /**
   * 比如我们自己写一个类重写了initPropertySources方法,在该方法中设置了一个环境变量的值为A
   * 启动的时候,我的环境变量中没有该值就会启动抛出异常
   */
  initPropertySources();
  /**
   * 用来校验我们容器启动必须依赖的环境变量的值
   */
  getEnvironment().validateRequiredProperties();
  /**
   * 创建一个早期事件监听器对象
   */
  if (this.earlyApplicationListeners == null) {
    this.earlyApplicationListeners = new LinkedHashSet < > (this.applicationListeners);
  } else {
    this.applicationListeners.clear();
    this.applicationListeners.addAll(this.earlyApplicationListeners);
  }
  this.earlyApplicationEvents = new LinkedHashSet < > ();
}

1,首先第一个是记录一下运行这个方法时的起始时间,最后通过方法结束获取的时间,来减去这个时间,就可以得到整个运行refresh方法的差值

this.startupDate = System.currentTimeMillis();   //容器启动的时间

2,随后就是设置两个标志位,一个是设置容器关闭的标志位,一个是设置容器激活的标志位

this.closed.set(false);   //容器关闭的标志位
this.active.set(true);    //容器激活的标志位

3,随后就是一个重要的方法 initPropertySources() ,该方法是一个空方法,主要是留给子类自己去实现,下面会专门举一个小demo来理解这个方法的作用。

protected void initPropertySources() {
  // For subclasses: do nothing by default.
}

4,随后一步就是先获取系统环境对象,在进入refresh方法之前就已经获取的 StandardEnvironment 实例对象,因此这里的环境对象不为空,直接将对象值返回。该对象内部包含了全部的系统变量和系统属性。

@Override
public ConfigurableEnvironment getEnvironment() {
  if (this.environment == null) {
    this.environment = createEnvironment();
  }
  return this.environment;
}

在拿到这个标准的环境对象之后,接下来就是通过这个validateRequiredProperties对里面的属性进行一个验证的操作

getEnvironment().validateRequiredProperties();

其对应的验证操作如下,会循环遍历这些属性,判断属性在系统变量中是否存在,如果不存在则抛出异常

@Override
public void validateRequiredProperties() {
  MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
  for (String key: this.requiredProperties) {
        //判断当前属性是否存在set集合中,即是否为系统属性
    if (this.getProperty(key) == null) {
            //如果当前属性不是系统属性,则添加一个异常
      ex.addMissingRequiredProperty(key);
    }
  }
    //如果异常数量不为空,则抛出异常
  if (!ex.getMissingRequiredProperties().isEmpty()) {
    throw ex;
  }
}

在这个抛出的异常中,就打印了这么一句话

The following properties were declared as required but could not be resolved: 

5,随后一步就是创建一个事件监听对象,在spring启动的时候,这个早期的earlyApplicationListeners 监听器对象默认为空,但是在springboot中,由于在spring.factories中存在大量的事件需要先加载,因此在springBoot中该对象不为空,相当于是在spring的基础上的一个增强。所以需要对这个earlyApplicationListeners对象判断是都为空。

if (this.earlyApplicationListeners == null) {
    //spring使用,默认为空
  this.earlyApplicationListeners = new LinkedHashSet <> (this.applicationListeners);
} else {
  // Reset local application listeners to pre-refresh state.
  //springboot使用,先清除原有的,再添加
    this.applicationListeners.clear();
  this.applicationListeners.addAll(this.earlyApplicationListeners);
}

如在spring.factories的文件中,默认就会注入这么多的监听器。该功能主要是springboot的一个扩展机制


276c28c2bb514c6cb54e95b2b1cfab39.png


6,除了这个监听器在spring启动时为空之外,这个早期Event事件也为空。

this.earlyApplicationEvents = new LinkedHashSet<>();

2,initPropertySources(可扩展)

由于initPropertySources 这个方法内部是一个空方法,主要是留给子类去实现,因此在这里,可以定义一个类继承 ClassPathXmlApplicationContext 这个类,如下

/**
 * @author zhenghuisheng
 * @date : 2023/6/6
 */
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
    //String... locations:xml配置文件的路径
  public MyClassPathXmlApplicationContext(String... locations) {
    super(locations);
  }
  @Override
  protected void initPropertySources() { / /初始化属性
    //设置属性
    getEnvironment().setRequiredProperties("USERNAME");
    //获取属性
    String requiredProperty = getEnvironment().getRequiredProperty("USERNAME");
    System.out.println("当前系统用户名称为:" + requiredProperty);
        //获取app名称
    String applicationName = getApplicationName();
    System.out.println("当前应用名称为:" + applicationName);
    //获取前置处理器
    List<BeanFactoryPostProcessor> list = getBeanFactoryPostProcessors();
    System.out.println("定义的bean工厂的后置处理器为的集合长度为:"+ list.size());
        ...
  }
}

在上一篇中有写到,在refresh方法之前的前置工作中,就会获取所有的系统环境和系统变量,如下图就是系统环境变量


104b300e3c7844c9ad9300ee59f1890b.png


因此这里可以直接拿到这些环境变量等。随后写一个主启动类进行测试

 public static void main(String[] args) {
    //扩展,用于鉴定属性
  ApplicationContext m = new MyClassPathXmlApplicationContext("classpath.a.xml");    
}

由于环境对象可以获取到,因此在这个环境变量里面设置一个属性

getEnvironment().setRequiredProperties("USERNAME");

上面设置的属性 USERNAME值,在设置之后会进行一个验证的操作,主要是验证该值在系统中是否存在,如果不存在则直接抛出异常。

void validateRequiredProperties() throws MissingRequiredPropertiesException;

开发中如果程序是需要包含某个值, 如需要某个属性或者某个前缀之类的,就可以在这一步进行判断,就不用进应用层里面去判断了。如在抽象类 AbstractEnvironment 中,有着这两个的具体实现,可以先设置值,随后会对设置的值进行验证。

//设置相关的属性值
@Override
public void setRequiredProperties(String...requiredProperties) {
  this.propertyResolver.setRequiredProperties(requiredProperties);
}
//验证属性值,属性在在系统属性中不存在,则立马抛出异常
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
  this.propertyResolver.validateRequiredProperties();
}

验证的流程具体如下:判断当前系统属性有没有这个值,如果系统有这个值,则不管;没有这个值,则抛出异常报错

@Override
public void validateRequiredProperties() {
  MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
  for (String key: this.requiredProperties) {
        //判断当前系统属性有没有这个值
    if (this.getProperty(key) == null) {
      ex.addMissingRequiredProperty(key);
    }
  }
    //如果系统有这个值,则不管;没有这个值,则抛出异常报错
  if (!ex.getMissingRequiredProperties().isEmpty()) {
    throw ex;
  }
}

主要作用就是提前做一个参数的校验,实际开发中用的也比较少。


3,总结

在这个prepareRefresh方法中,总结来说就是做了五件事情:设置容器启动时间、设置活跃状态为true、设置关闭状态为false、获取环境对象,并将当前系统的属性值设置到Environment对象中、实例化监听器对象和事件对象,并设置为空。


相关文章
|
16天前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
366 70
|
5月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
459 5
|
5月前
|
Java Spring 容器
两种Spring Boot 项目启动自动执行方法的实现方式
在Spring Boot项目启动后执行特定代码的实际应用场景中,可通过实现`ApplicationRunner`或`CommandLineRunner`接口完成初始化操作,如系统常量或配置加载。两者均支持通过`@Order`注解控制执行顺序,值越小优先级越高。区别在于参数接收方式:`CommandLineRunner`使用字符串数组,而`ApplicationRunner`采用`ApplicationArguments`对象。注意,`@Order`仅影响Bean执行顺序,不影响加载顺序。
386 2
|
6月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
105 0
|
2月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
695 0
|
6月前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
279 0
|
2月前
|
缓存 JSON 前端开发
第07课:Spring Boot集成Thymeleaf模板引擎
第07课:Spring Boot集成Thymeleaf模板引擎
330 0
第07课:Spring Boot集成Thymeleaf模板引擎
|
6月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
225 0

热门文章

最新文章