springboot启动流程源码分析(四)prepareEnvironment()

springboot源码分析系列文章

springboot启动流程源码之一(new SpringApplication(primarySources))

springboot启动流程源码分析(二)run(args)

springboot启动流程源码分析(三)getRunListeners()

源码解析

prepareEnvironment按字面意思就是准备环境,那到底准备什么环境呢?我们一起来慢慢看,其源代码如下

  private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //创建并配置相应的环境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //根据用户配置,配置 environment系统环境
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    // 启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器
    listeners.environmentPrepared(environment);
    //bindToSpringApplication绑定环境
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
      //环境转换
      //如果environment.class和模块使用的EnvironmentClass()不一致
      //那么转换成一样的
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
          deduceEnvironmentClass());
    }
    //将环境依附到PropertySources
    ConfigurationPropertySources.attach(environment);
    return environment;
  }

①getOrCreateEnvironment()

在这里插入图片描述


  // 获取或创建Environment,很显然我们这里是创建StandardServletEnvironment
  private ConfigurableEnvironment getOrCreateEnvironment() {
    // 存在则直接返回
    if (this.environment != null) {
      return this.environment;
    }
    // 根据webApplicationType创建对应的Environment
    switch (this.webApplicationType) {
    case SERVLET:
      // 标准的Servlet环境,也就是我们说的web环境
      return new StandardServletEnvironment();
    case REACTIVE:
      //REACTIVE环境
      return new StandardReactiveWebEnvironment();
    default:
      // 标准环境,非web环境
      return new StandardEnvironment();
    }
  }

②configureEnvironment(environment,applicationArguments.getSourceArgs());

在这里插入图片描述


  protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    //增加类型转换服务-默认为true
    if (this.addConversionService) {
      //返回ApplicationConversionService实例
      ConversionService conversionService = ApplicationConversionService.getSharedInstance();
      //设置类型转换服务
      environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    //加载配置源 及 命令行属性
    configurePropertySources(environment, args);
    //配置当前active的描述文件
    configureProfiles(environment, args);
  }

a.configurePropertySources(environment, args);
在这里插入图片描述

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
    //调用AbstractEnvironment的getPropertySources()方法
    //获取之前配置的所有属性
    MutablePropertySources sources = environment.getPropertySources();
     //如果this.defaultProperties不为null

    if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
      //那么添加defaultProperties到propertySources的末尾
      sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
    }
    //这里addCommandLineProperties默认为true 如果有命令行参数的数
    if (this.addCommandLineProperties && args.length > 0) {
      //name为:commandLineArgs
      String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
      //如果之前的MutablePropertySources中有name为commandLineArgs的PropertySource的话,则把当前命令行参数转换为CompositePropertySource类型,和原来的PropertySource进行合并,替换原来的PropertySource
      if (sources.contains(name)) {
        //如果sources中包含了"commandLineArgs",
        //那么将其替换为"springApplicationCommandLineArgs"
        PropertySource<?> source = sources.get(name);
        //先将"commandLineArgs"修改为null,
        CompositePropertySource composite = new CompositePropertySource(name);
        //然后新增一个PropertySource
        //name为"springApplicationCommandLineArgs",
        //source不变
        composite.addPropertySource(
                    new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
        composite.addPropertySource(source);
        //替换
        sources.replace(name, composite);
      }
      else {
        //如果之前没有name为commandLineArgs的PropertySource的话,则将其添加为MutablePropertySources中的第一个元素,注意了这里讲命令行参数添加为ConfigurableEnvironment中MutablePropertySources实例的第一个元素,且永远是第一个元素        
        sources.addFirst(new SimpleCommandLinePropertySource(args));
      }
    }
  }

b.configureProfiles(environment, args);
在这里插入图片描述


  //传入参数为StandardServletEnvironment和命令行参数
  protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    //调用的是AbstractEnvironment的getActiveProfiles()方法
    Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
    //再次获取和配置profile
    profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
    //设置environment的profile
    environment.setActiveProfiles(StringUtils.toStringArray(profiles));
  }

③ConfigurationPropertySources.attach(environment);

在这里插入图片描述


  //如果配置了configurationProperties属性, 那么将其放在environment的propertySources的首部
  public static void attach(Environment environment) {
    Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
    //取得environment中的propertySources
    MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
    PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
    if (attached != null && attached.getSource() != sources) {
      //如果存在的话,直接移除
      sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
      attached = null;
    }
    if (attached == null) {
      //将propertySources转换为SpringConfigurationPropertySources,放在首位
      sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
          new SpringConfigurationPropertySources(sources)));
    }
  }

④listeners.environmentPrepared(environment);

在这里插入图片描述
在这里插入图片描述
关于multicastEvent方法,详见springboot启动流程源码分析(三)getRunListeners()中详解。

  @Override
  public void environmentPrepared(ConfigurableEnvironment environment) {
    this.initialMulticaster
        .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
  }

⑤bindToSpringApplication(environment);

在这里插入图片描述


  protected void bindToSpringApplication(ConfigurableEnvironment environment) {
    try {
      //如果指定了main函数,那么会将当前环境绑定到指定的SpringApplication中
      Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
    }
    catch (Exception ex) {
      throw new IllegalStateException("Cannot bind to SpringApplication", ex);
    }
  }

⑥convertEnvironmentIfNecessary(environment,deduceEnvironmentClass())

在这里插入图片描述

//环境转换
  StandardEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment,
      Class<? extends StandardEnvironment> type) {
    if (type.equals(environment.getClass())) {
      return (StandardEnvironment) environment;
    }
    //environment.getClass()不是StandardEnvironment的实例
    return convertEnvironment(environment, type);
  }

  //环境转换
  private StandardEnvironment convertEnvironment(ConfigurableEnvironment environment,
      Class<? extends StandardEnvironment> type) {
    //新建一个StandardEnvironment实例
    //然后赋值
    StandardEnvironment result = createEnvironment(type);
    result.setActiveProfiles(environment.getActiveProfiles());
    result.setConversionService(environment.getConversionService());
    copyPropertySources(environment, result);
    return result;
  }

关注公众号
在这里插入图片描述
每周会更新干货知识

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值