springboot(2.4.1)源代码分析三prepareEnvironment

  |   0 评论   |   0 浏览

1598248028480090112.png
上一篇里面介绍了starting的逻辑,下面看看prepareEnvironment的逻辑,prepareEnvironment逻辑是用来初始化环境配置,并且通知各种listener进行对应的逻辑处理,这块也是基于事件驱动模式来做的,大逻辑跟starting完全一模一样,只是listeners有区别,看看prepareEnvironment到底有哪些listener,都做了什么操作。方法入口在org.springframework.boot.SpringApplication类中:

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//【1】prepareEnvironment
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			//【2】prepareEnvironment
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment); //设置spring.beaninfo.ignore为true,获取BeanInfo的时候采用Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO)
			Banner printedBanner = printBanner(environment); //打印banner
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			prepareContext(bootstrapContext, 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, listeners);
			throw new IllegalStateException(ex);
		}

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

	......

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment(); //StandardServletEnvironment
		configureEnvironment(environment, applicationArguments.getSourceArgs()); //配置type转换service、property源、以及profile(无代码)
		ConfigurationPropertySources.attach(environment); //添加ConfigurationPropertySourcesPropertySource
		listeners.environmentPrepared(bootstrapContext, environment); //广播environmentPrepared
		DefaultPropertiesPropertySource.moveToEnd(environment);
		configureAdditionalProfiles(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

environmentPrepared的listeners

  • org.springframework.boot.env.EnvironmentPostProcessorApplicationListener:触发spring.factories文件中定义的EnvironmentPostProcessor对象,目前跟踪到以下EnvironmentPostProcessor:
RandomValuePropertySourceEnvironmentPostProcessor:添加一个【RandomValuePropertySource {name='random'}】到environment对象的MutablePropertySources中,主要用于生成各种各样的随机数property的名字一定要以"random."前缀开头。

SystemEnvironmentPropertySourceEnvironmentPostProcessor:用新的【OriginAwareSystemEnvironmentPropertySource】替换之前的SystemEnvironmentPropertySource对象,OriginAwareSystemEnvironmentPropertySource实现了org.springframework.boot.origin.OriginLookup<String>接口,能够返回org.springframework.boot.origin.SystemEnvironmentOrigin对象,提供系统原生配置名,注意:springboot里面会处理系统配置的变量名(org.springframework.core.env.SystemEnvironmentPropertySource.resolvePropertyName(String)),把"."和"-"替换成"_"。


org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor:添加一个【org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor.JsonPropertySource】,用来处理spring.application.json配置的属性,优先级比SystemEnvironment配置的要高。

org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor:VCAP(vmware's cloud application platform),云上的一些应用可能会用到的虚拟化配置处理,spring.main.cloud-platform属性配置云平台名称,代码中有自动探测的逻辑,例如判断环境变量中是否存在KUBERNETES_SERVICE_HOST、KUBERNETES_SERVICE_PORT变量,或者是否存在这种_SERVICE_HOST、_SERVICE_PORT后缀结尾的变量。如果探测到是在虚拟化的环境中运行的程序,会读取vcap.application.和vcap.services.配置,加到commandLineArgs配置源后面去,也就是优先级比commandLineArgs低一级。

org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor:【重要逻辑类】Springboot运行环境处理器,判断springboot当前运行在哪个环境,参考spring.profiles.active配置,bean注入的时候,通过org.springframework.context.annotation.Profile注解指定在哪个运行环境生效。注入的bean可以通过org.springframework.context.EnvironmentAware获取到Environment,但是spring并不推荐直接使用Environment对象,而是通过属性注入${propertyName}的方式来使用。这个processor内部逻辑很复杂,有空需要多研究研究,运用了大量的函数式编程(T_T,感觉我是在看java代码吗),这里牵扯的地方太多了ConfigDataEnvironment、Binder、Environment、ConfigDataLoaders、ConfigDataEnvironmentContributors等等,后面还是需要专门的一篇文章来记录此内部逻辑。

org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor:reactor调试代理,通过spring.reactor.debug-agent.enabled开关来控制,默认是开启的,但是,同样要检查当前环境是否是reactor环境,reactor.tools.agent.ReactorDebugAgent这个class在不在classpath中,如果有class而且enable是true,那么就初始化reactor.tools.agent.ReactorDebugAgent实例,并且调用init方法。我们这里没有用reactor,因此不详细跟进。
  • org.springframework.boot.context.config.AnsiOutputApplicationListener:打印彩色日志的,通过spring.output.ansi.enabled属性来进行控制,三个选项DETECT(默认探测是否支持多彩日志)、ALWAYS、NEVER。
  • org.springframework.boot.context.logging.LoggingApplicationListener:初始化日志系统的listener,会用到logging.*配置,具体逻辑后面可以单独写一篇。
  • org.springframework.boot.autoconfigure.BackgroundPreinitializer:后台资源初始化监听,这个listener会事先初始化一些后台资源,主要涉及ConversionServiceInitializer、ValidationInitializer、MessageConverterInitializer、JacksonInitializer、CharsetInitializer这几类资源的初始化,可以通过设置spring.backgroundpreinitializer.ignore这个属性为true来关闭这个初始化逻辑。
  • org.springframework.boot.context.config.DelegatingApplicationListener:委派监听,会把这个ApplicationEvent事件播到context.listener.classes配置的自定义监听器上面。
  • org.springframework.boot.context.FileEncodingApplicationListener:文件编码监听,如果有设置spring.mandatory-file-encoding这个参数,则会跟file.encoding这个系统参数做对比,如果不一致,则抛出异常。如果没有设置spring.mandatory-file-encoding,则此listener没任何效果。

environment对象的MutablePropertySources

environment对象有一个MutablePropertySources属性,里面包含了很多个propertySource,下面是经过listener处理完过后的PropertySource列表,按优先级排序:

  • ConfigurationPropertySourcesPropertySource {name='configurationProperties'}:
  • SimpleCommandLinePropertySource {name='commandLineArgs'}:命令行参数
  • StubPropertySource {name='servletConfigInitParams'}:servlet初始化参数
  • StubPropertySource {name='servletContextInitParams'}:ServletContext初始化参数
  • PropertiesPropertySource {name='systemProperties'}:JVM的System系统属性
  • OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}:操作系统的环境变量
  • RandomValuePropertySource {name='random'}:随机生成的带random.*前缀的属性
    下面两个是加载的classpath:/config/application.yml或者classpath:/config/application.properties,调试环境的spring.profiles.active=dev
  • OriginTrackedMapPropertySource {name='Config resource 'class path resource [application-dev.yml]' via location 'optional:classpath:/''}
  • OriginTrackedMapPropertySource {name='Config resource 'class path resource [application.yml]' via location 'optional:classpath:/''}

PropertySourcesPropertyResolver获取配置的逻辑:

protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if (this.propertySources != null) {
			for (PropertySource<?> propertySource : this.propertySources) {  //按优先级挨个propertySource取,取到值就返回
				if (logger.isTraceEnabled()) {
					logger.trace("Searching for key '" + key + "' in PropertySource '" +
							propertySource.getName() + "'");
				}
				Object value = propertySource.getProperty(key);
				if (value != null) {
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					return convertValueIfNecessary(value, targetValueType);
				}
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Could not find key '" + key + "' in any property source");
		}
		return null;
	}

标题:springboot(2.4.1)源代码分析三prepareEnvironment
作者:michael
地址:https://blog.junxworks.cn/articles/2021/06/24/1624503462571.html