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;
  }

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

<think>嗯,用户让我结合源码总结SpringBoot启动流程。首先,我得回忆一下SpringBoot启动的关键步骤,然后对应到源码里的具体类方法。可能需要从main方法开始,逐步分析。 首先,SpringBoot应用的入口通常是带有@SpringBootApplication注解的主类,然后调用SpringApplication.run()方法。这一步应该触发整个启动流程。需要确认SpringApplication类的结构,特别是run方法里面的步骤。 我记得SpringApplication的初始化阶段会加载应用上下文,读取配置,可能还涉及到事件监听机制。比如,准备环境、创建应用上下文、刷新上下文这些步骤。需要具体到源码中的方法调用顺序,比如prepareEnvironment、refreshContext等。 另外,自动配置是SpringBoot的重要特性,所以需要提到@EnableAutoConfiguration如何通过spring.factories加载配置类。可能涉及到SpringFactoriesLoader的源码,还有条件注解的处理,比如@ConditionalOnClass。 还有内嵌Servlet容器的启动,比如Tomcat,这部分可能在刷新应用上下文的时候触发,具体在ServletWebServerApplicationContext中。需要看看如何初始化Tomcat,并启动它。 用户要求结合源码,所以最好能提到关键的类方法的名称,比如SpringApplication.run()、ApplicationContextInitializer、ApplicationListener、ConfigurableEnvironment、AbstractApplicationContext.refresh()等。 可能需要分步骤详细说明每个阶段做了什么,比如实例化SpringApplication,配置环境,准备上下文,执行Runners等。还要注意事件驱动的部分,比如ApplicationStartingEvent在run方法的不同阶段发布事件,通知监听器。 另外,用户可能希望了解启动流程中的扩展点,比如ApplicationRunnerCommandLineRunner的执行时机,或者自定义的InitializersListeners如何被调用。 总结的时候要结构清晰,按启动顺序分点,每个点对应源码中的关键步骤,并简要说明其作用。确保覆盖从启动类开始到应用完全启动的整个流程,包括自动配置、Bean加载、内嵌服务器启动等核心环节。</think>结合Spring Boot 2.x源码,我将核心启动流程总结如下(关键方法用`代码块`标注): --- ### 一、入口阶段 ```java @SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); // 入口方法 } } ``` --- ### 二、SpringApplication初始化 1. **资源加载** ```java // SpringApplication构造方法 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 判断WEB环境 ``` 2. **加载初始化器** ```java setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // META-INF/spring.factories加载 ``` --- ### 三、run()方法核心流程 ```java public ConfigurableApplicationContext run(String... args) { // 1. 启动计时器 StopWatch stopWatch = new StopWatch(); // 2. 准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArgs); // 3. 打印Banner Banner printedBanner = printBanner(environment); // 4. 创建应用上下文 context = createApplicationContext(); // 5. 准备上下文 prepareContext(context, environment, listeners, applicationArgs, printedBanner); // 6. 刷新上下文(核心) refreshContext(context); // 7. 执行Runners callRunners(context, applicationArgs); return context; } ``` --- ### 、关键扩展点实现 1. **自动配置原理** ```java // @SpringBootApplication -> @EnableAutoConfiguration List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 加载spring.factories中的配置类 // AutoConfigurationImportSelector.selectImports() List<String> configurations = SpringFactoriesLoader.loadFactoryNames( EnableAutoConfiguration.class, this.beanClassLoader); ``` 2. **内嵌Web容器启动** ```java // ServletWebServerApplicationContext.refresh() WebServer webServer = startWebServer(); // 启动Tomcat/Jetty/Undertow // TomcatServletWebServerFactory.getWebServer() Tomcat tomcat = new Tomcat(); tomcat.start(); // 启动Tomcat ``` --- ### 五、事件驱动机制 ```java // 事件发布顺序: 1. ApplicationStartingEvent 2. ApplicationEnvironmentPreparedEvent 3. ApplicationContextInitializedEvent 4. ApplicationPreparedEvent 5. ApplicationStartedEvent 6. ApplicationReadyEvent // 监听器实现 public class MyListener implements ApplicationListener<ApplicationEvent> { @Override public void onApplicationEvent(ApplicationEvent event) { // 处理事件 } } ``` --- ### 六、启动流程图 ``` main() ├─ SpringApplication构造 │ ├─ 推断WEB类型 │ └─ 加载Factories配置 ├─ run() │ ├─ 准备Environment │ ├─ 创建ApplicationContext │ ├─ 刷新Context(核心) │ │ ├─ BeanFactory初始化 │ │ ├─ 执行BeanFactoryPostProcessor │ │ ├─ 注册BeanPostProcessor │ │ ├─ 初始化MessageSource │ │ ├─ 初始化事件广播器 │ │ ├─ 初始化Servlet容器 │ │ └─ 发布ContextRefreshedEvent │ └─ 执行Runners └─ 返回运行中的Context ``` --- ### 七、核心源码类 1. `SpringApplication`:启动入口 2. `ConfigurableApplicationContext`:应用上下文 3. `AbstractApplicationContext.refresh()`:刷新方法 4. `SpringFactoriesLoader`:工厂加载器 5. `AutoConfigurationImportSelector`:自动配置选择器 建议结合`org.springframework.boot.SpringApplication``org.springframework.context.support.AbstractApplicationContext`源码阅读,重点关注`refresh()`方法的12个标准步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值