springboot(2.4.1)源代码分析三prepareEnvironment
上一篇里面介绍了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