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

简介: 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中的核心方法,文章会慢慢更新的,文章可能会有些理解不到位的地方,大家有更好的见解可以交流学习!


目录
相关文章
|
2月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
4月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
6月前
|
XML 人工智能 Java
Spring IOC 到底是什么?
IOC(控制反转)是一种设计思想,主要用于解耦代码,简化依赖管理。其核心是将对象的创建和管理交给容器处理,而非由程序直接硬编码实现。通过IOC,开发者无需手动new对象,而是由框架负责实例化、装配和管理依赖对象。常见应用如Spring框架中的BeanFactory和ApplicationContext,它们实现了依赖注入和动态管理功能,提升了代码的灵活性与可维护性。
198 1
|
XML Java 数据格式
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
|
Java Spring 容器
获取Spring的ApplicationContext的几种方式
简单来说就是Spring中的高级容器,可以获取容器中的各种bean组件,注册监听事件,加载资源文件等功能。
251 0
获取Spring的ApplicationContext的几种方式
|
Java 容器 Spring
获取Spring的ApplicationContext的几种方式
Application Context定义 简单来说就是Spring中的高级容器,可以获取容器中的各种bean组件,注册监听事件,加载资源文件等功能。 具体定义可以参考官网:https://spring.
1091 0
|
5月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
968 0
|
6月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
723 0

热门文章

最新文章