Spring Cloud Config原码篇(十)
2021-06-11 13:04
标签:using available pat 形式 数组 方案 tom 系统 filter 上篇中说到通过@Value注解获取配置中心的内容进行注入,要想了解这个就要知道spring Environment原理,关于这原理我看了下网上分析的文章:https://blog.csdn.net/topdeveloperr/article/details/88063828 首先来看第一部分,就是spring boot需要解析的外部资源文件的路径是如何初始化的。在spring boot的启动流程中,有一个 prepareEnvironment 方法,这个方法就是用来准备Environment这个对象的。找他的入中是从springApplication.run 开始的 进入ConfigurableEnvironment类 这个方法主要就是创建和配置spring容器的环境信息 进入getOrCreateEnvironment看它是怎么创建环境的,进去后发现这个方法,就是根据当前的webApplication类型匹配对应的environment,当前默认的应该就是StandardServletEnvironment ,如果是spring webflux,则是StandardReactiveWebEnvironment . StandardServletEnvironment 是整个spring boot应用运行环境的实现类,后面所有的关于环境相关的配置操作都是基于这个类,它的类的结构图如下 StandardServletEnvironment的初始化过程会做一些事情,就是配置一些基本的属性来源。StandardServletEnvironment 会初始化父类 AbstractEnvironment ,在这个类的构造方法中,会调用一个自定义配置文件的方法,这个是spring中比较常见的实现手法,前面在看ribbon、eureka中都有看到。 customizePropertySources 这个方法被 StandardServletEnvironment 重写了,所以会调用StandardServletEnvironment 中的 customizePropertySources 方法。不难看出,这里是将几个不同的配置源封装成 StubPropertySource 添加到 继续调用父类,也就是 StandardEnvironment 类中的 customizePropertySources 方法。SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME: 系统变量,通过System.setProperty设置的变量,默认可以看到 java.version 、 os.name 等。SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME: 系统环境变量,也就是我们配置JAVA_HOME的地方。 这里要明确一点,就是添加PropertySource的目的其实就是要告诉Environment,解析哪些位置的属性文件进行加载。而在这个添加过程中,所有的添加都是基于 addLast ,也就是最早添加的PropertySource会放在最前面。 systemEnvironment 是在 systemProperties 前面,这点很重要。因为前面的配置会覆盖后面的配置,也就是说系统变量中的配置比系统环境变量中的配置优先级更高 在上面的代码中可以看到,所有的外部资源配置都是添加到了一个MutablePropertySources对象中,这个对象封装了属性资源的集合。而从 MutablePropertySources 命名来说,Mutable是一个可变的意思,也就是意味着它动态的管理了PropertySource的集合。 接着,我们来看一下它怎么用的,找到 AbstractEnvironment 这个类,在这里定义了MutablePropertySources。并且把这个MutablePropertySources作为参数传递给了ConfigurablePropertyResolver 配置解析器中,而这个配置解析器是一个PropertySourcesPropertyResolver 实例。 通过下面类图可以发现AbstractEnvironment 实现了文件解析器ConfigurablePropertyResolver ,而在上面这段代码中我们把 MutablePropertySources 传递到PropertySourcesPropertyResolver 中。这样就可以让 AbstractEnvironment 具备文件解析的功能,只是这个功能,委托给了PropertySourcesPropertyResolver来实现。 通过上面的代码,spring构造了一个 StandardServletEnvironment 对象并且初始化了一些需要解析的propertySource,现在回退到SpringApplication类的prepareEnvironment方法,我们继续来看 configureEnvironment 这个方法,这个方法有两个作用 configurePropertySources方法中 到目前为止,还是在初始化外部化配置的数据来源。接着进入configureProfiles方法,这个方法就比较容易理解,就是配置当前激活的profiles,将当前的activeProfiles设置到enviroment中。这样就能够使得我们完成不同环境下配置的获取问题。 经过上面的操作spring的配置信息都已加载完成,但有一个很重要的配置还没有加载,那就是springboot的配置信息,现在回退到SpringApplication类的prepareEnvironment类 选择EventPublishingRunListener类的environmentPrepared,进入事件的监听 选择multicastEvent,进入SimpleApplicationEventMulticaster类的multicastEvent方法,这个方法是多纬度的监听 上面事件有反射调用就一定会有一个监听,如果有兴趣可以Debugger会发现这个getApplicationListeners的事件监听中有一个叫ConfigFileApplicationListener,这个监听器就是用来处理项目配置的,进入ConfigFileApplicationListener类会看到一个onApplicationEvent方法 如果有人在我之前说的要debugger的地方debugger的话会发现,现在发布的事件是一个ApplicationEnvironmentPreparedEvent事件,进入onApplicationEnvironmentPreparedEvent事件中 最终执行到 ConfigFileApplicationListener.addPropertySources 方法中 这个方法做两个事情 进入Load类load方法这个方法比较复杂,总的来说,就是加载所有可能的profiles首先我们来看,这里实际上是调用了 FilteredPropertySource.apply 方法。然后传递了一个lambda表达式到apply方法中。 下面这个lambda表达式的主要逻辑是 点击上面的 initializeProfiles,进入Load类initializeProfiles方法,该方法的作用是加载存在已经激活的 profiles 这个看明白后返回上一层点load进入Load类的load方法,继续跟进load方法,通过 getSearchLoacations 进行搜索,并且进行迭代。 getSearchLocations的主要功能,就是获取需要遍历的目标路径,默认情况下,会去DEFAULT_SEARCH_LOCATIONS中查找,也就是 拿到路径地址之后,再拼接对应路径,选择合适的yml或者properties解析器进行解析。 整理流程如下: 2)遍历所有的路径,拼装配置文件名称。 3)再遍历解析器,选择yml或者properties解析,将解析结果添加到集合MutablePropertySources当中。 至此,springBoot中的资源文件加载完毕,解析顺序从上到下,所以前面的配置文件会覆盖后面的配置文件。可以看到 application.properties 的优先级最低,系统变量和环境变量的优先级相对较高 在Spring Cloud Config中,通过@Value注解注入了一个属性,但是这个属性不存在于本地配置中,那么Config是如何将远程配置信息加载到Environment中的呢?这里需要思考几个问题 为了解决这三个问题,Spring Cloud Config规范中定义了三个核心的接口 下面就来了解下Environment是如何在启动过程中从远程服务器上加载配置的 从前面的代码分析过程中我们知道,Environment中所有外部化配置,针对不同类型的配置都会有与之对应的PropertySource,比如(SystemEnvironmentPropertySource、CommandLinePropertySource)。以及PropertySourcesPropertyResolver来进行解析。 在spring boot项目启动时,有一个prepareContext的方法,它会回调所有实现了ApplicationContextInitializer 的实例,来做一些初始化工作。 前面说过prepareContext的方法,它会回调所有实现了ApplicationContextInitializer 的实例然而PropertySourceBootstrapConfiguration 实现了 ApplicationContextInitializer 接口,其目的就是在应用程序上下文初始化的时候做一些额外的操作.根据默认的 AnnotationAwareOrderComparator 排序规则对propertySourceLocators数组进行排序,获取运行的环境上下文ConfigurableEnvironment,遍历propertySourceLocators时 source不为空的情况,才会设置到environment中 选择locateCollection进入PropertySourceLoader类的locateCollection方法;这个方法会调用子类的locate方法,来获得一个PropertySource,然后将PropertySource集合返回。接着它会调用 ConfigServicePropertySourceLocator 的locate方法。 进入ConfigServicePropertySourceLocator类的locate方法这个就是Config Client的关键实现了,它会通过RestTemplate调用一个远程地址获得配置信息,getRemoteEnvironment 。然后把这个配置PropertySources,然后将这个信息包装成一个OriginTrackedMapPropertySource,设置到 Composite 中。 服务器端去远程仓库加载配置的流程就比较简单了,核心接口是: EnvironmentRepository ,提供了配置读取的功能。先从请求入口开始看;pring Cloud Config Server提供了EnvironmentController,这样通过在浏览器访问即可从git中获取配置信息;在这个controller中,提供了很多的映射,最终会调用的是 getEnvironment 。 this.repository.findOne ,调用某个repository存储组件来获得环境配置信息进行返回。repository是一个 EnvironmentRepository 对象,它有很多实现,其中就包含RedisEnvironmentRepository 、 JdbcEnvironmentRepository 等。默认实现是MultipleJGitEnvironmentRepository ,表示多个不同地址的git数据源。在MultipleJGitEnvironmentRepository类中 代理遍历每个 JGitEnvironmentRepository,JGitEnvironmentRepository 下使用 NativeEnvironmentRepository 代理读取本地文件。 在AbstractScmEnvironmentRepository类findOne方法中调用抽象类的findOne方法,主要有两个核心逻辑 Spring Cloud Config原码篇(十) 标签:using available pat 形式 数组 方案 tom 系统 filter 原文地址:https://www.cnblogs.com/xing1/p/14221358.html一、Environment的初始化
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners
listeners,ApplicationArguments
applicationArguments) {
// 根据上下文,创建一个合适的Environment对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置Environment的propertySource、以及profile
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 通知监听器,加载配置文件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new
EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment
,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
}
MutablePropertySources 中,调用 addLast 是表示一直往最后的位置添加。SERVLET_CONFIG_PROPERTY_SOURCE_NAME:servlet的配置信息,也就是在中配置的SERVLET_CONTEXT_PROPERTY_SOURCE_NAME: 这个是servlet初始化的上下文,也就是以前我们在web.xml中配置的 context-param 。JNDI_PROPERTY_SOURCE_NAME: 加载jndi.properties配置信息。protected void customizePropertySources(MutablePropertySources propertySources)
{
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()));
}
二、MutablePropertySources
public class MutablePropertySources implements PropertySources {
private final List
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
private final MutablePropertySources propertySources = new
MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
}
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
ConversionService conversionService =
ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)
conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties",
this.defaultProperties));
}
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));
}
}
}
protected void configureProfiles(ConfigurableEnvironment environment, String[]
args) {
Set
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//springboot的发布事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
} void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
//得到结果集
for (ApplicationListener> listener : getApplicationListeners(event, type)) {
if (executor != null) {
//反射调用
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
} @Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
//环境的准备事件
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
//Appliaction的准备事件
onApplicationPreparedEvent(event);
}
} private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES,
LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList();
this.processedProfiles = new LinkedList
();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile,
this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
(defaultProperties) -> {
// 未处理的数据集合
this.profiles = new LinkedList();
// 已处理的数据集合
this.processedProfiles = new LinkedList();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap();
//加载存在已经激活的 profiles
initializeProfiles();
while (!this.profiles.isEmpty()) {//遍历所有profiles
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
// 确定搜索范围,获取对应的配置文件名,并使用相应加载器加载
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
// 将处理完的 profile添加到 processedProfiles列表当中,表示已经处理完成
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);// 更新 activeProfiles列表
private void initializeProfiles() {
// The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
this.profiles.add(null);
Binder binder = Binder.get(this.environment);
//判断当前环境是否配置 spring.profiles.active属性
Set
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isDirectory = location.endsWith("/");
Set
private Set
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isDirectory = location.endsWith("/");
Set
1)获取默认的配置文件路径,有4种。 三、config中的environment
3.1、Config Client 配置加载过程
那Config Client在启动的时候,必然也会需要从远程服务器上获取配置加载到Environment中,这样才能使得应用程序通过@value进行属性的注入,而且我们一定可以猜测到的是,这块的工作一定又和spring中某个机制有关系。public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection
//回调所有实现
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;
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
List
static Collection
public org.springframework.core.env.PropertySource> locate(
org.springframework.core.env.Environment environment) {
ConfigClientProperties properties =
this.defaultProperties.override(environment);
CompositePropertySource composite = new
OriginTrackedCompositePropertySource(
"configService");
RestTemplate restTemplate = this.restTemplate == null
? getSecureRestTemplate(properties) : this.restTemplate;
Exception error = null;
String errorBody = null;
try {
String[] labels = new String[] { "" };
if (StringUtils.hasText(properties.getLabel())) {
labels = StringUtils
.commaDelimitedListToStringArray(properties.getLabel());
}
String state = ConfigClientStateHolder.getState();
// Try all the labels until one works
for (String label : labels) {
Environment result = getRemoteEnvironment(restTemplate, properties,
label.trim(), state);
if (result != null) {
log(result);
// result.getPropertySources() can be null if using xml
if (result.getPropertySources() != null) {
for (PropertySource source : result.getPropertySources()) {
@SuppressWarnings("unchecked")
Map
四、Config Server获取配置过程
public Environment getEnvironment(String name, String profiles, String label,
boolean includeOrigin) {
name = Environment.normalize(name);
label = Environment.normalize(label);
Environment environment = this.repository.findOne(name, profiles, label,
includeOrigin);
if (!this.acceptEmpty
&& (environment == null || environment.getPropertySources().isEmpty()))
{
throw new EnvironmentNotFoundException("Profile Not found");
}
return environment;
}
@Override
public Environment findOne(String application, String profile, String label,
boolean includeOrigin) {
//遍历所有Git源
for (PatternMatchingJGitEnvironmentRepository repository :
this.repos.values()) {
if (repository.matches(application, profile, label)) {
for (JGitEnvironmentRepository candidate :
getRepositories(repository,
application, profile, label)) {
try {
if (label == null) {
label = candidate.getDefaultLabel();
}
Environment source = candidate.findOne(application, profile,
label,
includeOrigin);
if (source != null) {
return source;
}
}
catch (Exception e) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"Cannot load configuration from " +
candidate.getUri()
+ ", cause: (" + e.getClass().getSimpleName()
+ ") " + e.getMessage(),
e);
}
continue;
}
}
}
}
JGitEnvironmentRepository candidate = getRepository(this, application,
profile,
label);
if (label == null) {
label = candidate.getDefaultLabel();
}
if (candidate == this) {
return super.findOne(application, profile, label, includeOrigin);
}
return candidate.findOne(application, profile, label, includeOrigin);
}
@Override
public synchronized Environment findOne(String application, String profile,
String label, boolean includeOrigin) {
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(
getEnvironment(), new NativeEnvironmentProperties());
Locations locations = getLocations(application, profile, label);
delegate.setSearchLocations(locations.getLocations());
Environment result = delegate.findOne(application, profile, "",
includeOrigin);
result.setVersion(locations.getVersion());
result.setLabel(label);
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
getUri());
}
下一篇:python中while循环