Spring加载流程源码分析02【setConfigLocations】

简介: 1 setConfigLocations主要工作有两个:创建环境对象ConfigurableEnvironment 、处理ClassPathXmlApplicationContext传入的字符串中的占位符; 2 环境对象ConfigurableEnvironment中包含了当前JVM的profile配置信息、环境变量、 Java进程变量; 3 处理占位符的关键是ConfigurableEnvironment、PropertyResolver、PropertyPlaceholderHelper之间的配合


类图

image.png

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
    throws BeansException {
  // 1.初始化父类
  super(parent);
  // 2.设置本地的配置信息
  setConfigLocations(configLocations);
  // 3.完成Spring容器的初始化
  if (refresh) {
    refresh();
  }
}

setConfigLocations

public void setConfigLocations(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++) {
      //循环取出每一个path参数,在此处就一个“applicationContext.xml“”
      this.configLocations[i] = resolvePath(locations[i]).trim();
    }
  }
  else {
    this.configLocations = null;
  }
}

  1  setConfigLocations主要工作有两个:创建环境对象ConfigurableEnvironment 、处理ClassPathXmlApplicationContext传入的字符串中的占位符;

   2 环境对象ConfigurableEnvironment中包含了当前JVM的profile配置信息、环境变量、 Java进程变量;

   3 处理占位符的关键是ConfigurableEnvironment、PropertyResolver、PropertyPlaceholderHelper之间的配合

// 这个方法的目的是替换掉path字符串中的占位符${XXX}这样的内容
protected String resolvePath(String path) {
     // 1.进入getEnvironment()
     // 2.进入resolveRequiredPlaceholders方法
  return getEnvironment().resolveRequiredPlaceholders(path);
}

1.ConfigurableEnvironment

 getEnvironment():创建了ConfigurableEnvironment 对象

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

image.png提供的方法中可以看出两个功能

   处理profile:Profile是对测试、生产等不同环境下的bean配置,这里我们没有特别设置,所以用到的profile是AbstractEnvironment的defaultProfiles; 之前介绍IOC的时候也介绍过profile。

   处理property

   获取系统环境信息

   合并环境信息

2.PropertyResolver

resolveRequiredPlaceholders(path)

image.pngimage.png处理占位符的方法

image.png

3.PropertyPlaceholderHelper

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
  if (this.strictHelper == null) {
    // 创建PropertyPlaceholderHelper对象
    this.strictHelper = createPlaceholderHelper(false);
  }
  return doResolvePlaceholders(text, this.strictHelper);
}

进入doResolvePlaceholders继续查看

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
  return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
    @Override
    public String resolvePlaceholder(String placeholderName) {
      // 
      return getPropertyAsRawString(placeholderName);
    }
  });
}

getPropertyAsRawString的具体实现在PropertySourcesPropertyResolver类中

@Override
protected String getPropertyAsRawString(String key) {
  return getProperty(key, String.class, false);
}

继续跟踪helper.replacePlaceholders(),到了PropertyPlaceholderHelper.parseStringValue方法,这里面逐一找出每个占位符去做替换:

public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
  Assert.notNull(value, "'value' must not be null");
  return parseStringValue(value, placeholderResolver, new HashSet<String>());
}

parseStringValue方法中,找到了占位符后,会调用入参placeholderResolver的resolvePlaceholder(placeholder)方法,也就是上面匿名类的getPropertyAsRawString方法(实际上就是PropertySourcesPropertyResolver.getPropertyAsRawString方法),最终会在PropertySourcesPropertyResolver.getProperty方法中找出所有的属性来匹配占位符

protected String parseStringValue(
      String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
    StringBuilder result = new StringBuilder(strVal);
    int startIndex = strVal.indexOf(this.placeholderPrefix);
    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.add(originalPlaceholder)) {
          throw new IllegalArgumentException(
              "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
        }
        // Recursive invocation, parsing placeholders contained in the placeholder key.
        placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
        // Now obtain the value for the fully resolved key...
        String propVal = placeholderResolver.resolvePlaceholder(placeholder);
        if (propVal == null && this.valueSeparator != null) {
          int separatorIndex = placeholder.indexOf(this.valueSeparator);
          if (separatorIndex != -1) {
            String actualPlaceholder = placeholder.substring(0, separatorIndex);
            String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
            propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
            if (propVal == null) {
              propVal = defaultValue;
            }
          }
        }
        if (propVal != null) {
          // Recursive invocation, parsing placeholders contained in the
          // previously resolved placeholder value.
          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 string value \"" + strVal + "\"");
        }
        visitedPlaceholders.remove(originalPlaceholder);
      }
      else {
        startIndex = -1;
      }
    }
    return result.toString();
  }

image.png

至此setConfigLocations的代码就查看到此,后面介绍refresh的代码


相关文章
|
11天前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
34 1
|
26天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
31 1
|
27天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
27 1
|
1月前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
224 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
24天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
22 0
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
182 2
|
2月前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
|
1月前
|
JSON 前端开发 JavaScript
优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。
|
2月前
|
消息中间件 NoSQL 安全
(转)Spring Boot加载 不同位置的 application.properties配置文件顺序规则
这篇文章介绍了Spring Boot加载配置文件的顺序规则,包括不同位置的application.properties文件的加载优先级,以及如何通过命令行参数或环境变量来指定配置文件的名称和位置。
下一篇
无影云桌面