SpringBoot源码学习(三) 环境准备

运行Spring应用程序

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   // 配置无头属性, 设置该应用程序即使没有检测到显示器也允许启动
   configureHeadlessProperty();
   // 从`META-INF/spring.factories`文件集中获取SpringApplicationRunListener的子类listener
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 开启事件监听,通知监听者们(listener)执行相应操作
   listeners.starting();
   try {
      // 封装命令行参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 准备环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      // 配置忽略信息
      configureIgnoreBeanInfo(environment);
      // 打印banner
      Banner printedBanner = printBanner(environment);
      // 创建应用上下文
      context = createApplicationContext();
      // 从`META-INF/spring.factories`文件集中获取SpringBootExceptionReporter的子类
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      // 准备上下文
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      // 刷新上下
      refreshContext(context);
      // 刷新上下文后置处理
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

准备环境

通过观察者模式将环境变量,命令行参数,配置文件属性绑定到Application。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // 创建和配置环境
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 配置环境
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 将环境链接到configurationProperties属性名下
   ConfigurationPropertySources.attach(environment);
   // 发布环境准备事件
   listeners.environmentPrepared(environment);
   // 绑定到Application
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

依赖图谱

prepareEnvironment

创建和配置环境

根据SpringApplication构造器中推断出应用类型来实例化不同的环境。参考: SpringBoot源码学习(一) SpringApplication构造器

private ConfigurableEnvironment getOrCreateEnvironment() {
   if (this.environment != null) {
      return this.environment;
   }
   switch (this.webApplicationType) {
   case SERVLET:
      // 标准的Servlet环境
      return new StandardServletEnvironment();
   case REACTIVE:
      // WebFlux环境
      return new StandardReactiveWebEnvironment();
   default:     
      return new StandardEnvironment();
   }
}

根据依赖关系实例化StandardServletEnvironment需实例化父类。推断出首先执行org.springframework.core.env.AbstractEnvironment#AbstractEnvironment

public AbstractEnvironment() {
   // 创建一个新的Environment实例,在构造过程中回调customPropertySources(MutablePropertySources),以允许子类在适当时操纵PropertySource实例。
   customizePropertySources(this.propertySources);
}

自定义属性源

org.springframework.web.context.support.StandardServletEnvironment重写了customizePropertySources(MutablePropertySources) 方法。

@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
   /**
    * 在servletConfigInitParams中存在的属性将优先于在servletContextInitParams中的属性,
    * 并且在上述任何一个中找到的属性都优先于在jndiProperties中发现的属性。 
    * 以上任何属性均优先于StandardEnvironment超类提供的系统属性和环境变量。 
    * 与Servlet相关的属性源在此阶段作为`StubPropertySource stubs`添加,
    * 一旦实际的ServletContext对象被initPropertySources(ServletContext, ServletConfig)完全初始化变得可用。
    * servletConfigInitParams
    * servletContextInitParams
    * jndiProperties 不一定存在
    * systemProperties
    * systemEnvironment
    */
   propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
   propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
   if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
      propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
   }
   super.customizePropertySources(propertySources);
}
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
   propertySources.addLast(
         new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
   propertySources.addLast(
         new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

执行完此处代码,源集合中的name为:

servletConfigInitParams,
servletContextInitParams,
systemProperties,
systemEnvironment

配置环境

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
   if (this.addConversionService) {
      // 获取数据转换共享实例
      ConversionService conversionService = ApplicationConversionService.getSharedInstance();
      environment.setConversionService((ConfigurableConversionService) conversionService);
   }
   // 配置属性源
   configurePropertySources(environment, args);
   // 配置Profiles
   configureProfiles(environment, args);
}

配置属性源

在此应用程序的环境中添加,删除或重新排序PropertySource。

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
   MutablePropertySources sources = environment.getPropertySources();
   if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
      // this.defaultProperties 不为空,将defaultProperties放入sources尾
      sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
   }

   // 如果命令行参数存在,判断名称为commandLineArgs的source存不存在,存在替换,不存在放入sources首,参数以命令行优先
   if (this.addCommandLineProperties && args.length > 0) {
      String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
      if (sources.contains(name)) {
         PropertySource<?> source = sources.get(name);
         CompositePropertySource composite = new CompositePropertySource(name);
         composite.addPropertySource(
               new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
         composite.addPropertySource(source);
         sources.replace(name, composite);
      }
      else {
         sources.addFirst(new SimpleCommandLinePropertySource(args));
      }
   }
}

配置Profiles

配置文件环境中哪些配置文件处于活动状态。在配置文件处理过程中,可以通过spring.profiles.active属性激活其他配置文件。

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
   // 配置文件环境中哪些配置文件处于活动状态。在配置文件处理过程中,可以通过spring.profiles.active属性激活其他配置文件。
   Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
   profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
   environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
   if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
      Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
      System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
   }
}

链接数据源

将环境源链接到configurationProperties属性名下

public static void attach(Environment environment) {
   Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
   MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
   // 获取sources中名称为configurationProperties的sources,存在将并与现有sources不同内存地址,就删除configurationProperties对应的源。不存在直接将此名称添加到队首
   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) {
      sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
            new SpringConfigurationPropertySources(sources)));
   }
}

发布环境准备事件

参考: SpringBoot源码学习(二) 发布启动事件

void environmentPrepared(ConfigurableEnvironment environment) {
	/**
	 * org.springframework.boot.devtools.restart.RestartApplicationListener,
	 * org.springframework.boot.context.config.ConfigFileApplicationListener,
	 * org.springframework.boot.context.config.AnsiOutputApplicationListener,
	 * org.springframework.boot.context.logging.LoggingApplicationListener,
	 * org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
	 * org.springframework.boot.autoconfigure.BackgroundPreinitializer,
	 * org.springframework.boot.context.config.DelegatingApplicationListener,
	 * org.springframework.boot.context.FileEncodingApplicationListener
	 */
   for (SpringApplicationRunListener listener : this.listeners) {
      listener.environmentPrepared(environment);
   }
}
posted @ 2021-01-22 11:52  Chinda  阅读(213)  评论(0编辑  收藏  举报