这篇文章主要介绍了SpringCloud之配置刷新的原理,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
我们知道在SpringCloud中,当配置变更时,我们通过访问http://xxxx/refresh,可以在不启动服务的情况下获取最新的配置,那么它是如何做到的呢,当我们更改数据库配置并刷新后,如何能获取最新的数据源对象呢?下面我们看SpringCloud如何做到的。
一、环境变化
1.1、关于ContextRefresher
当我们访问/refresh时,会被RefreshEndpoint类所处理。我们来看源代码:
/* * Copyright 2013-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.endpoint; import java.util.Arrays; import java.util.Collection; import java.util.Set; import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; /** * @author Dave Syer * @author Venil Noronha */ @ConfigurationProperties(prefix = "endpoints.refresh", ignoreUnknownFields = false) @ManagedResource public class RefreshEndpoint extends AbstractEndpoint<Collection> { private ContextRefresher contextRefresher; public RefreshEndpoint(ContextRefresher contextRefresher) { super("refresh"); this.contextRefresher = contextRefresher; } @ManagedOperation public String[] refresh() { Set keys = contextRefresher.refresh(); return keys.toArray(new String[keys.size()]); } @Override public Collection invoke() { return Arrays.asList(refresh()); } }
通过源代码我们了解到:当访问refresh端点时,实际上执行的是ContextRefresher的refresh方法,那么我们继续追踪源代码,找到其refresh方法:
public synchronized Set refresh() { Map before = extract( this.context.getEnvironment().getPropertySources()); addConfigFilesToEnvironment(); Set keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet(); this.context.publishEvent(new EnvironmentChangeEvent(context, keys)); this.scope.refreshAll(); return keys; }
我们可以看到refresh方法做了如下几件事情:
1)获取刷新之前的所有PropertySource
2) 调用addConfigFilesToEnvironment方法获取最新的配置
3) 调用changes方法更新配置信息
4) 发布EnvironmentChangeEnvent事件
5)调用refreshScope的refreshAll方法刷新范围
我们重点关注一下2,3,4步骤
1.2、addConfigFilesToEnvironment方法
我们先来看看这个方法是怎么实现的:
/* for testing */ ConfigurableApplicationContext addConfigFilesToEnvironment() { ConfigurableApplicationContext capture = null; try { StandardEnvironment environment = copyEnvironment( this.context.getEnvironment()); SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class) .bannerMode(Mode.OFF).web(false).environment(environment); // Just the listeners that affect the environment (e.g. excluding logging // listener because it has side effects) builder.application() .setListeners(Arrays.asList(new BootstrapApplicationListener(), new ConfigFileApplicationListener())); capture = builder.run(); if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) { environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE); } MutablePropertySources target = this.context.getEnvironment() .getPropertySources(); String targetName = null; for (PropertySource source : environment.getPropertySources()) { String name = source.getName(); if (target.contains(name)) { targetName = name; } if (!this.standardSources.contains(name)) { if (target.contains(name)) { target.replace(name, source); } else { if (targetName != null) { target.addAfter(targetName, source); } else { // targetName was null so we are at the start of the list target.addFirst(source); targetName = name; } } } } } finally { ConfigurableApplicationContext closeable = capture; while (closeable != null) { try { closeable.close(); } catch (Exception e) { // Ignore; } if (closeable.getParent() instanceof ConfigurableApplicationContext) { closeable = (ConfigurableApplicationContext) closeable.getParent(); } else { break; } } } return capture; }
1) 该方法首先拷贝当前的Environment
2) 通过SpringApplicationBuilder构建了一个简单的SpringBoot启动程序并启动
builder.application().<strong style="color:transparent">来源gaodaima#com搞(代@码网</strong>setListeners(Arrays.asList(new BootstrapApplicationListener(), new ConfigFileApplicationListener()));
这里面会添加两个监听器分别为:BootstrapApplicationList
以上就是SpringCloud配置刷新原理解析的详细内容,更多请关注gaodaima搞代码网其它相关文章!