https://www.cnblogs.com/junzisi/p/15480619.html
原文链接:https://blog.csdn.net/justuseit/article/details/115124277
https://blog.csdn.net/yaomingyang/article/details/109318562
https://blog.csdn.net/shijianjinghun/article/details/110492890
https://www.cnblogs.com/junzisi/p/15505983.html
第一阶段:获取事件监听器
起点-> new SpringApplication()
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))
-> SpringApplication.setListeners()
this.listeners = new ArrayList<>(listeners)
listeners = {ArrayList@1489} size = 12
0 = {BootstrapApplicationListener@1660}
1 = {LoggingSystemShutdownListener@1661}
2 = {EnvironmentPostProcessorApplicationListener@1662}
3 = {AnsiOutputApplicationListener@1663}
4 = {LoggingApplicationListener@1664}
5 = {BackgroundPreinitializer@1665}
6 = {DelegatingApplicationListener@1666}
7 = {RestartListener@1667}
8 = {ParentContextCloserApplicationListener@1668}
9 = {AppListener@1669}
10 = {ClearCachesApplicationListener@1670}
11 = {FileEncodingApplicationListener@1671}
发布环境准备事件
-> SpirngApplication.prepareEnvironment()
getOrCreateEnvironment() 获取配置环境对象
configureEnvironment() 对AbstractEnvironment环境对象的propertySources属性进行赋值
0 = {PropertySource$StubPropertySource@1933} "StubPropertySource {name='servletConfigInitParams'}"
1 = {PropertySource$StubPropertySource@1934} "StubPropertySource {name='servletContextInitParams'}"
2 = {PropertiesPropertySource@1935} "PropertiesPropertySource {name='systemProperties'}"
3 = {SystemEnvironmentPropertySource@1936} "SystemEnvironmentPropertySource {name='systemEnvironment'}"
一个servlet配置属性源,一个servlet上下文属性源,一个操作系统环境变量属性源,一个jvm环境变量属性源
listeners.environmentPrepared() 通过SpringApplication的启动生命周期发布ApplicationEnvironmentPreparedEvent事件,
EnvironmentPostProcessorApplicationListener会监听onApplicationEnvironmentPreparedEvent事件,
回调到ConfigDataEnvironmentPostProcessor的postProcessEnvironment(),在其中从spring.factories中
获取ConfigDataLoader,ConfigDataLocationResolver 加载解析核心组件,并构造成ConfigDataEnvironment对象
-> listeners.environmentPrepared(bootstrapContext, environment) 发送环境已准备事件ApplicationEnvironmentPreparedEvent:
对application.properties/application.yaml的配置文件解析
第二阶段:在监听器EnvironmentPostProcessorApplicationListener的onApplicationEvent处理环境准备事件
-> EnvironmentPostProcessorApplicationListener.onApplicationEvent()
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent();
}
if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
进入EnvironmentPostProcessorApplicationListener的onApplicationEvent方法,前面发送的就是ApplicationEnvironmentPreparedEvent事件
-> EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent()
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
进入onApplicationEnvironmentPreparedEvent,这里面会获取环境后置处理器,也可以叫环境增强器,EnvironmentPostProcessor然后遍历执行
getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())
result = {ArrayList@2276} size = 12
0 = {RandomValuePropertySourceEnvironmentPostProcessor@2278}
1 = {CachedRandomPropertySourceEnvironmentPostProcessor@2279}
2 = {SystemEnvironmentPropertySourceEnvironmentPostProcessor@2280} 把环境上下文中名称为'systemEnvironment'的PropertySource替换掉
3 = {SpringApplicationJsonEnvironmentPostProcessor@2281} 将系统属性spring.application.json或SPRING_APPLICATION_JSON解析成JSON,
并存储到Environment
4 = {CloudFoundryVcapEnvironmentPostProcessor@2282}
5 = {HostInfoEnvironmentPostProcessor@2283}
6 = {ConfigDataEnvironmentPostProcessor@2284} 重点:
7 = {BootstrapConfigFileApplicationListener@2285} 重点:
8 = {NacosConfigDataMissingEnvironmentPostProcessor@2286}
9 = {DebugAgentEnvironmentPostProcessor@2287}
10 = {IntegrationPropertiesEnvironmentPostProcessor@2288}
11 = {DecryptEnvironmentPostProcessor@2289}
获取环境后置处理器,遍历执行,其实我们真正关注的,是对配置文件进行解析的类是ConfigDataEnvironmentPostProcessor。
以前我们的配置文件的优先级是由ConfigFileApplicationListener处理的;从2.4开始,改成了ConfigDataEnvironmentPostProcessor。
------------------------------------------------------------------------------------进入ConfigDataEnvironmentPostProcessor
-> ConfigDataEnvironmentPostProcessor.postProcessEnvironment()
getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply()
-> new ConfigDataEnvironment()
new ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles) {
Binder binder = Binder.get(environment); //1. 绑定当前Environment对象
UseLegacyConfigProcessingException.throwIfRequested(binder);
this.logFactory = logFactory;
this.logger = logFactory.getLog(getClass());
//2. 从属性spring.config.on-not-found中获取文件找不到的执行逻辑
this.notFoundAction = binder.bind(ON_NOT_FOUND_PROPERTY, ConfigDataNotFoundAction.class)
.orElse(ConfigDataNotFoundAction.FAIL);
this.bootstrapContext = bootstrapContext;
this.environment = environment;
//3. 从spring.factories中获取ConfigDataLocationResolver实现。(可以自己实现,扩展点之一)
//4. 同时这里面会传入boostrapper/resourceLoader/Binder等参数用于构造参数反射
this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
this.additionalProfiles = additionalProfiles;
//5. 从spring.factories中获取所有的ConfigDataLoader并用反射进行实例化
this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext);
//6. 创建ConfigDataEnvironmentContributors对象,里面会根据spring.config.import / location等默认定位参数初始化Contributor
this.contributors = createContributors(binder);
}
-> new ConfigDataLocationResolvers()
-> new ConfigDataLoaders()
-> new ConfigDataEnvironmentContributors()
-> ConfigDataEnvironment.processAndApply() 开始加载配置文件逻辑
void processAndApply() {
//1. 封装ConfigDataImporter对象,里面有解析ConfigDataLocation -> ConfigDataResource 和load ConfigDataResource -> ConfigData之类的操作
ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
this.loaders);
this.bootstrapContext.register(Binder.class, InstanceSupplier
.from(() -> this.contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE)));
//1. 加载和解析ConfigDataLocation -> ConfigDataResource -> ConfigData ,此时还没有导入到Environment中,
执行完毕之后应该都是BOUND_IMPORT,且此时绑定了spring.config / spring.profiles相关的配置属性信息
ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
//2. 获取包含Root Contributor中 所有ConfigurationPropertySource的Binder
Binder initialBinder = contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
//3. 重新注册Binder到Bootstrapper中
this.bootstrapContext.register(Binder.class, InstanceSupplier.of(initialBinder));
ConfigDataActivationContext activationContext = createActivationContext(initialBinder); //构建激活的上下文对象,此时对元计算平台进行设置
//4. 带云计算平台参数上下文进行二次迭代
contributors = processWithoutProfiles(contributors, importer, activationContext);
//5. 构建profile
activationContext = withProfiles(contributors, activationContext);
//6. 带profile参数进行第三次迭代
contributors = processWithProfiles(contributors, importer, activationContext);
//7. 应用到Environment对象中
applyToEnvironment(contributors, activationContext);
}
-> ConfigDataEnvironment.processInitial()
三大步
1、通过ConfigDataLocationResolver将相关spring.config.import,spring.config.addtional-location,spring.config.location等资源定位路径下
的spring.config.name-{profile}.fileExtension资源解析成ConfigDataResource
2、通过ConfigDataLoader将ConfigDataLocationResolver解析好的资源进行加载,将ConfigDataResource -> ConfigData , 其中ConfigData是一组ProeprtySource
3、将加载好的ConfigData添加到Environment中
private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer) {
this.logger.trace("Processing initial config data environment contributors without activation context");
contributors = contributors.withProcessedImports(importer, null);
registerBootstrapBinder(contributors, null, DENY_INACTIVE_BINDING);
return contributors;
}
-> ConfigDataEnvironmentContributors.withProcessedImports()
处理 Kind为INITIAL_IMPORT类型的Contributros ,这里面也是主要的解析配置的地方
调用ConfigDataEnvironmentContributors.withProcessedImports()
ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
ConfigDataActivationContext activationContext) {
//1. 获取Import阶段,分导入前导入后
ImportPhase importPhase = ImportPhase.get(activationContext);
this.logger.trace(LogMessage.format("Processing imports for phase %s. %s", importPhase,
(activationContext != null) ? activationContext : "no activation context"));
ConfigDataEnvironmentContributors result = this;
int processed = 0;
while (true) {
//1阶段. 初始化为null
//2阶段. 设置好ActivationContext(相关云计算平台参数进行第二轮的迭代),进行相关云平台过滤
//3阶段. 进行profile文件的解析和加载
ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
if (contributor == null) {
this.logger.trace(LogMessage.format("Processed imports for of %d contributors", processed));
return result;
}
if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
//从UNBOUND_IMPORT Contributor中获取配置属性源
Iterable<ConfigurationPropertySource> sources = Collections
.singleton(contributor.getConfigurationPropertySource());
// 进行占位符解析
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
result, activationContext, true);
Binder binder = new Binder(sources, placeholdersResolver, null, null, null);
ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(binder);
// 绑定ConfigDataProperties 并进行替换
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, bound));
continue;
}
//2.封装Resolver,Loader等相关操作上下文对象
ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
result, contributor, activationContext);
ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
//3. 从ConfigDataLocationContributor(ConfigDataProperties)中获取ConfigDataLocation(资源路径对象)
List<ConfigDataLocation> imports = contributor.getImports();
this.logger.trace(LogMessage.format("Processing imports %s", imports));
//4. 解析到Map<ConfigDataResource, ConfigData>
Map<ConfigDataResource, ConfigData> imported = importer.resolveAndLoad(activationContext,
locationResolverContext, loaderContext, imports); 解析configDataLocation为ConfigDataResource,
随后ConfingDataLoader#load为ConfigData,并返回Map映射关系
this.logger.trace(LogMessage.of(() -> imported.isEmpty() ? "Nothing imported" : "Imported "
+ imported.size() + " resource " + ((imported.size() != 1) ? "s" : "") + imported.keySet()));
ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
asContributors(imported));
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext, //返回设置好Child Contributor的结果集,然后继续下一次迭代
result.getRoot().withReplacement(contributor, contributorAndChildren));
processed++;
}
}
-> ConfigDataImporter.resolveAndLoad()
Map<ConfigDataResource, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
List<ConfigDataLocation> locations) {
try {
//1. 初始化import阶段profile为空 , 这个第三阶段会派上用场
Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
//2. 使用ConfigDateLocationResolver进行加载和解析
// ConfigDataResolutionResult 包含了ConfigDataLocation 和ConfigDataResource(解析结果)
List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
//3. 使用ConfigDataLoader将ConfigDataResource -> ConfigData -> (PropertySource)
return load(loaderContext, resolved);
}
catch (IOException ex) {
throw new IllegalStateException("IO error on loading imports from " + locations, ex);
}
}
-> ConfigDataImporter.resolve()
private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolver<?> resolver,
ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) {
//进行解析
List<ConfigDataResolutionResult> resolved = resolve(location, () -> resolver.resolve(context, location));
if (profiles == null) {
return resolved;
}
//下面是第三阶段用来进行profile环境加载
List<ConfigDataResolutionResult> profileSpecific = resolve(location,
() -> resolver.resolveProfileSpecific(context, location, profiles));
return merge(resolved, profileSpecific);
}
-> ConfigDataImport.load() 使用刚才加载到的ConfigDataResource列表进行ConfigDataLoader#load加载
//我们可以通过在META-INF/spring.factories中配置我们自己实现的ConfigDataLoader进行扩展加载其他格式的外部化环境,
// 比如最后我会演示扩展实现一个加载json文件的Loader
private Map<ConfigDataResource, ConfigData> load(ConfigDataLoaderContext loaderContext,
List<ConfigDataResolutionResult> candidates) throws IOException {
Map<ConfigDataResource, ConfigData> result = new LinkedHashMap<>();
//1. 从后向前迭代ConfigDataResolutionResult(包含ConfigDataLocation,ConfigDataResource)
//2. 这里有个细节,为什么是从后往前遍历?因为之前解析profile的时候是从优先级低 -> 高
for (int i = candidates.size() - 1; i >= 0; i--) {
ConfigDataResolutionResult candidate = candidates.get(i);
ConfigDataLocation location = candidate.getLocation();
ConfigDataResource resource = candidate.getResource();
if (this.loaded.add(resource)) {
try {
//2. ConfigDataLoader加载将ConfigDataResource -> ConfigData (PropetySource)又是一个扩展点
ConfigData loaded = this.loaders.load(loaderContext, resource); loaders = {ArrayList@4421} size = 3
0 = {ConfigTreeConfigDataLoader@4424}
1 = {StandardConfigDataLoader@4425}
2 = {NacosConfigDataLoader@4426}
if (loaded != null) {
result.put(resource, loaded);
}
}
catch (ConfigDataNotFoundException ex) {
handle(ex, location);
}
}
}
return Collections.unmodifiableMap(result);
}
->
至此,核心的三步我们就完成了两步,解析和加载,随后就是一些重复逻辑,加载另外两阶段的配置,这边挑一些细节来展示,
我们回到ConfigDataEnvironment#processAndApply(),刚刚执行完processInitia()方法逻辑,解析和加载了第一阶段,
随后进行云计算厂商的配置整合,核心在createActivationContext()
执行完processInitial方法回到processAndApply方法,执行createActivationContext()
-> ConfigDataEnvironment.createActivationContext()
-> new ConfigDataActivationContext()
new ConfigDataActivationContext(Environment environment, Binder binder) {
this.cloudPlatform = deduceCloudPlatform(environment, binder);
this.profiles = null;
}
-> ConfigDataActivationContext.deduceCloudPlatform()
private CloudPlatform deduceCloudPlatform(Environment environment, Binder binder) {
for (CloudPlatform candidate : CloudPlatform.values()) {
//尝试从Environment上下文中获取spring.main.cloud-platform,若有指定对应的云计算厂商则直接返回对应的CloudPlatform
if (candidate.isEnforced(binder)) {
return candidate;
}
}
//从环境变量中寻找是否有对应云平台的环境变量参数,比如k8s(svc相关环境参数): KUBERNETES_SERVICE_HOST/KUBERNETES_SERVICE_PORT
return CloudPlatform.getActive(environment);
}
-> ConfigDataEnvironment.processWithoutProfiles()
-> ConfigDataEnvironment.withProfiles()
private ConfigDataActivationContext withProfiles(ConfigDataEnvironmentContributors contributors, 获取Environment中的profile属性
ConfigDataActivationContext activationContext) {
this.logger.trace("Deducing profiles from current config data environment contributors");
Binder binder = contributors.getBinder(activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
try {
//优先设置构造SpringApplication的addtionalProfile
Set<String> additionalProfiles = new LinkedHashSet<>(this.additionalProfiles);
//设置include profile
additionalProfiles.addAll(getIncludedProfiles(contributors, activationContext));
//设置active profile 、 default profile
Profiles profiles = new Profiles(this.environment, binder, additionalProfiles);
return activationContext.withProfiles(profiles);
}
catch (BindException ex) {
if (ex.getCause() instanceof InactiveConfigDataAccessException) {
throw (InactiveConfigDataAccessException) ex.getCause();
}
throw ex;
}
-> ConfigDataEnvironment.processWithProfiles() 带profile参数进行第三次迭代
contributors = processWithProfiles(contributors, importer, activationContext);
-> ConfigDataEnvironment.applyToEnvironment(contributors, activationContext) 应用到Environment对象中
------------------------------------------------------------------------------------进入BootstrapConfigFileApplicationListener
既是事件监听器,也是EnvironmentPostProcessor后置处理器