Spring IOC源码:ApplicationContext刷新前准备工作

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring IOC源码:ApplicationContext刷新前准备工作

文章目录

Spring源码系列:

前言

正文

方法1:super(parent);

方法2: AbstractApplicationContext:

方法3: setParent

方法4:setConfigLocations

方法5: resolvePath

方法6:getEnvironment

方法7:createEnvironment

方法8:StandardEnvironment

方法9 resolveRequiredPlaceholders

方法10 resolveRequiredPlaceholders

方法11 doResolvePlaceholders

总结

Spring源码系列:

Spring IOC源码:简单易懂的Spring IOC 思路介绍

Spring IOC源码:核心流程介绍

Spring IOC源码:ApplicationContext刷新前准备工作

Spring IOC源码:obtainFreshBeanFactory 详解(上)

Spring IOC源码:obtainFreshBeanFactory 详解(中)

Spring IOC源码:obtainFreshBeanFactory 详解(下)

Spring IOC源码:<context:component-scan>源码详解

Spring IOC源码:invokeBeanFactoryPostProcessors 后置处理器详解

Spring IOC源码:registerBeanPostProcessors 详解

Spring IOC源码:实例化前的准备工作

Spring IOC源码:finishBeanFactoryInitialization详解

Spring IoC源码:getBean 详解

Spring IoC源码:createBean( 上)

Spring IoC源码:createBean( 中)

Spring IoC源码:createBean( 下)

Spring IoC源码:finishRefresh 完成刷新详解

前言

在进入spring正式核心逻辑处理前,Spring IOC需要提前进行些初始化工作,为后续的操作准备好一些环境。下面主要讲解这部分的内容,也是refresh()方法前的代码逻辑,即下面的super(parent),setConfigLocations(configLocations);本人以xml配置的方式,入口为ClassPathXmlApplicationContext;

  public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {
    //调用父类方法进行初始化
    super(parent);
    //对location文件路径进行解析、替换占位符等工作
    setConfigLocations(configLocations);
    if (refresh) {
      refresh();
    }
  }

super(parent); 见方法1详解

setConfigLocations(configLocations); 见方法4详解

正文

方法1:super(parent);

6b7643dacc70adbd489b9feb5b1e6be1_acd39a3e84ed440aa9a39c72b0f0d58a.png

可以看到ClassPathXmlApplicationContext最上面的父类为AbstractApplicationContext,这里一直往上调用直到AbstractApplicationContext父类中进行初始化工作,

  public AbstractApplicationContext(@Nullable ApplicationContext parent) {
    this();
    setParent(parent);
  }

this(); 调用无参构造方法 见方法2

setParent(parent); 设置此上下文的父类 见方法3

方法2: AbstractApplicationContext:

  public AbstractApplicationContext() {
    this.resourcePatternResolver = getResourcePatternResolver();
  }


  protected ResourcePatternResolver getResourcePatternResolver() {
    //返回一个返回用于解析占位符的解析器
    return new PathMatchingResourcePatternResolver(this);
  }

方法3: setParent

    public void setParent(@Nullable ApplicationContext parent) {
    //赋值传进来的父上下文到当前上下文属性中
    this.parent = parent;
    if (parent != null) {
      //获取父容器系统环境属性
      Environment parentEnvironment = parent.getEnvironment();
      if (parentEnvironment instanceof ConfigurableEnvironment) {
        //合并到当前上下文中
        getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
      }
    }
  }

方法4:setConfigLocations

  public void setConfigLocations(@Nullable String... locations) {
    if (locations != null) {
      Assert.noNullElements(locations, "Config locations must not be null");
      //备注:将我们传进来的配置文件信息,填充到此上下文中
      this.configLocations = new String[locations.length];
      for (int i = 0; i < locations.length; i++) {
        //备注:遍历解析处理配置文件路径,看是否需要用到系统环境变量进行占位符替换
        this.configLocations[i] = resolvePath(locations[i]).trim();
      }
    }
    else {
      this.configLocations = null;
    }
  }

this.configLocations[i] = resolvePath(locations[i]).trim(); 见方法5

方法5: resolvePath

  protected String resolvePath(String path) {
    //获取上下文中的系统环境属性,并调用其路径解析方法,如含有占位符:${path}
    return getEnvironment().resolveRequiredPlaceholders(path);
  }

getEnvironment() 见方法6

resolveRequiredPlaceholders(path) 见方法9

方法6:getEnvironment

  public ConfigurableEnvironment getEnvironment() {
    if (this.environment == null) {
      //创建系统环境属性
      this.environment = createEnvironment();
    }
    return this.environment;
  }


createEnvironment(); 见方法7

方法7:createEnvironment

  protected ConfigurableEnvironment createEnvironment() {
    //创建一个标准的环境属性
    return new StandardEnvironment();
  }

new StandardEnvironment(); 见方法8

方法8:StandardEnvironment


cad2eb42b6255739b6eb95453562fd59_f8c5c8e905c9430db79ae65b264e5662.png

进入这个类之后,我们看到它并没有构造方法,而且也没有resolveRequiredPlaceholders()方法,不过我们可以看到它继承了AbstractEnvironment方法,所以实例化时,肯定是调用父类的构造器。

方法9 resolveRequiredPlaceholdersresolveRequiredPlaceholders方法其实调用的是父类AbstractEnvironment中的方法,其类图关系如下:


  @Override
  public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    return this.propertyResolver.resolveRequiredPlaceholders(text);
  }

resolveRequiredPlaceholders(text) 见方法10

方法10 resolveRequiredPlaceholders

  @Override
  public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    if (this.strictHelper == null) {
      //创建占位符解析器
      this.strictHelper = createPlaceholderHelper(false);
    }
    //解析并返回处理后的配置文件路径
    return doResolvePlaceholders(text, this.strictHelper);
  }

doResolvePlaceholders(text, this.strictHelper) 见方法11

方法11 doResolvePlaceholders

  public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
    Assert.notNull(value, "'value' must not be null");
    return parseStringValue(value, placeholderResolver, null);
  }
  protected String parseStringValue(
      String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
    //这里的this.placeholderPrefix 默认值为${
    int startIndex = value.indexOf(this.placeholderPrefix);
    //判断下传进来的路径是否有${开头的占位符
    if (startIndex == -1) {
      return value;
    }
    StringBuilder result = new StringBuilder(value);
    while (startIndex != -1) {
      //查询一下是否有结束占位符下标
      int endIndex = findPlaceholderEndIndex(result, startIndex);
      if (endIndex != -1) {
        //获取占位符内容
        String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
        String originalPlaceholder = placeholder;
        if (visitedPlaceholders == null) {
          visitedPlaceholders = new HashSet<>(4);
        }
        if (!visitedPlaceholders.add(originalPlaceholder)) {
          throw new IllegalArgumentException(
              "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
        }
        // 递归调用,看占位符内容是否还有包含占位符 ,比如${${user}}
        placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
        // 通过占位符内容名称去查询出,获取具体值,如user从environment中查出当前电脑名称
        String propVal = placeholderResolver.resolvePlaceholder(placeholder);
        if (propVal == null && this.valueSeparator != null) {
          //如果查不到可能是以键值对的形式 如 name:zhangsan
          //这时候我们要解析出KEY跟VALUE
          //获取:符号的下标
          int separatorIndex = placeholder.indexOf(this.valueSeparator);
          if (separatorIndex != -1) {
            //获取出key的值,如name
            String actualPlaceholder = placeholder.substring(0, separatorIndex);
            //获取出value的值,如zhangsan
            String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
            //通过key值名称去查询,如果查不到则以actualPlaceholder为默认值进行赋值
            propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
            if (propVal == null) {
              propVal = defaultValue;
            }
          }
        }
        if (propVal != null) {
          //递归调用,解析包含在以前解析的占位符值中的占位符。
          propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
          result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
          if (logger.isTraceEnabled()) {
            logger.trace("Resolved placeholder '" + placeholder + "'");
          }
          startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
        }
        else if (this.ignoreUnresolvablePlaceholders) {
          // Proceed with unprocessed value.
          startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
        }
        else {
          throw new IllegalArgumentException("Could not resolve placeholder '" +
              placeholder + "'" + " in value \"" + value + "\"");
        }
        visitedPlaceholders.remove(originalPlaceholder);
      }
      else {
        startIndex = -1;
      }
    }
    return result.toString();
  }

至此占位符解析结束,返回解析后的值赋值给上下文的configLocations数组;

总结

refresh方法调用前的解析工作大概就是这样,这里只是Spring IOC的流程,如果是Springboot的话,会有一些方法重载,代码会更复杂。刷新前的准备工作,主要就是创建StandEnvironment属性类,解析占位符等操作;后面的文章再来写下refresh中的核心方法,文章会慢慢更新的,文章可能会有些理解不到位的地方,大家有更好的见解可以交流学习!


目录
相关文章
|
4天前
|
JavaScript Java 关系型数据库
自主版权的Java诊所管理系统源码,采用Vue 2、Spring Boot等技术栈,支持二次开发
这是一个自主版权的Java诊所管理系统源码,支持二次开发。采用Vue 2、Spring Boot等技术栈,涵盖患者管理、医生管理、门诊管理、药店管理、药品管理、收费管理、医保管理、报表统计及病历电子化等功能模块。
|
10天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
10天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
5天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
15 0
|
10天前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
24 0
|
2月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
4月前
|
XML Java 数据格式
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)
44 1
|
1月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
169 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
4月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
50 0
|
2月前
|
XML Java 数据格式
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
这篇文章详细介绍了Spring框架中IOC容器的Bean管理,特别是基于XML配置方式的实现。文章涵盖了Bean的定义、属性注入、使用set方法和构造函数注入,以及如何注入不同类型的属性,包括null值、特殊字符和外部bean。此外,还探讨了内部bean的概念及其与外部bean的比较,并提供了相应的示例代码和测试结果。
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)