Springboot 中使用阿里云ACM配置中心
当我们的项目只是一个简单的Springboot项目时,想实现Springboot启动时加载读取远程的数据库或者redis等配置信息时,使用阿里云ACM配置中心就有诸多不便了。官方提供的sdk包要么需要使用SpringCloud或者是刚出来的nocos做注册中心;因此只能自力更生了。
实现原理
参考了一下SpringCloud的实现方式,在下面这个类中实现了配置信息的重载:org.springframework.cloud.bootstrap.BootstrapApplicationListener
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered
{
...
[@Override](https://my.oschina.net/u/1162528)
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
....
}
...
这个类实现了一个ApplicationEnvironmentPreparedEvent
的Listener
的接口,并在spring.factories
文件进行了配置,这样在springboot启动加载准备系统配置信息时就会自动加载这个类。这个类在此时就会去加载和更新相关配置信息,既然可以这样操作那么我们也一定可以做个类似的简化操作。
去调试springboot启动时的过程时,你会发现这个类:org.springframework.boot.context.config.ConfigFileApplicationListener
它会去根据序号执行所有的EnvironmentPostProcessor
的实现类,而这些正好是springboot读取的相关配置的操作,同时这个类的配置根据order
序号正好是最后一个;那么我们也实现这个接口并将其序号设置在这个类的后面。更详细的可以去查看springboot的启动原理。
具体实现:
在项目中的resources目录下创建META-INF
目录,在里面创建一个spring.factories
文件(不用担心被其他jar包的覆盖)中编写如下配置:
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=top.vchar.alibaba.acm.ACMConfigEnvironmentPostProcessor
创建ACMConfigEnvironmentPostProcessor类,示例代码如下:
springboot2.x的实现
public class ACMConfigEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
private final DeferredLog logger = new DeferredLog();
/**
* The default order for the processor.
*
* 这将ConfigFileApplicationListener的order值作为初始值+1
*/
public static final int DEFAULT_ORDER = ConfigFileApplicationListener.DEFAULT_ORDER + 1;
private int order = DEFAULT_ORDER;
[@Override](https://my.oschina.net/u/1162528)
public int getOrder() {
return this.order;//order越大越后执行
}
[@Override](https://my.oschina.net/u/1162528)
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
// 获取所有springboot的application配置
List<PropertySource<?>> applicationConfig = propertySources.stream().filter(p -> p instanceof OriginTrackedMapPropertySource && p.getName().contains("applicationConfig")).collect(Collectors.toList());
if(applicationConfig.size()==0){
logger.warn("do not find applicationConfig PropertySource");
return;
}
//获取配置的acm信息
AcmProperties acmProperties = new AcmProperties();
if(applicationConfig.size()==1){
// only one application config
loadAcmConfig((OriginTrackedMapPropertySource) applicationConfig.get(0), acmProperties);
}else {
for(PropertySource<?> propertySource: applicationConfig){
loadAcmConfig((OriginTrackedMapPropertySource) propertySource, acmProperties);
}
String[] activeProfiles = environment.getActiveProfiles();
if(activeProfiles!=null && activeProfiles.length>0){
List<PropertySource<?>> activeSources = propertySources.stream().filter(p -> p instanceof OriginTrackedMapPropertySource && p.getName().contains("applicationConfig") && p.getName().contains(activeProfiles[0])).collect(Collectors.toList());
if(activeSources.size() > 0){
loadAcmConfig((OriginTrackedMapPropertySource) activeSources.get(0), acmProperties);
}
}
}
// TODO 从系统环境变量中获取acm信息覆盖配置文件中的
...
//根据配置信息初始化acm配置服务,使用官方demo即可
...
//拉取远程acm配置的信息
Map<String, Object> config = loadConfig(acmProperties);
if(!config.isEmpty()){
newSource.putAll(config);
}
//将获取到配置信息更新到当前环境中
for(PropertySource<?> propertySource: applicationConfig){
OriginTrackedMapPropertySource source = (OriginTrackedMapPropertySource) propertySource;
source.getSource().putAll(newSource);
}
}
}
springboot1.x的实现(以下是和springboot2.x的不同的地方)
// 获取所有springboot的application配置
PropertySource<?> applicationConfigurationProperties = propertySources.get("applicationConfigurationProperties");
if(null==applicationConfigurationProperties){
logger.warn("don't find applicationConfigurationProperties info");
return;
}
//从配置环境中获取ACM的配置信息
loadAcmConfig(applicationConfigurationProperties);
从配置环境中获取ACM的配置信息
private AcmProperties loadAcmConfig(PropertySource<?> applicationConfigurationProperties){
AcmProperties acmProperties = new AcmProperties();
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.application-data-id")){
acmProperties.setApplicationDataId(applicationConfigurationProperties.getProperty("alibaba.acm.application-data-id").toString());
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.group")){
acmProperties.setGroup(applicationConfigurationProperties.getProperty("alibaba.acm.group").toString());
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.endpoint")){
acmProperties.setEndpoint(applicationConfigurationProperties.getProperty("alibaba.acm.endpoint").toString());
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.namespace")){
acmProperties.setNamespace(applicationConfigurationProperties.getProperty("alibaba.acm.namespace").toString());
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.access-key")){
acmProperties.setAccessKey(applicationConfigurationProperties.getProperty("alibaba.acm.access-key").toString());
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.secret-key")){
acmProperties.setSecretKey(applicationConfigurationProperties.getProperty("alibaba.acm.secret-key").toString());
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.time-out")){
acmProperties.setTimeOut(Integer.parseInt(applicationConfigurationProperties.getProperty("alibaba.acm.time-out").toString()));
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.ram-role-name")){
acmProperties.setRamRoleName(applicationConfigurationProperties.getProperty("alibaba.acm.ram-role-name").toString());
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.data-id-list")){
String dataIds = applicationConfigurationProperties.getProperty("alibaba.acm.data-id-list").toString().trim();
if(dataIds.length()>0){
acmProperties.setDataIdList(Arrays.asList(dataIds.split(",")));
}
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.open-kms-filter")){
if("true".equals(applicationConfigurationProperties.getProperty("alibaba.acm.open-kms-filter").toString())){
acmProperties.setOpenKMSFilter(true);
}else {
acmProperties.setOpenKMSFilter(false);
}
}else {
acmProperties.setOpenKMSFilter(false);
}
if(null!=applicationConfigurationProperties.getProperty("alibaba.acm.region-id")){
acmProperties.setRegionId(applicationConfigurationProperties.getProperty("alibaba.acm.region-id").toString());
}
return acmProperties;
}
刷新配置环境中的配置信息
List<EnumerableCompositePropertySource> sourceList = (List<EnumerableCompositePropertySource>) applicationConfigurationProperties.getSource();
EnumerableCompositePropertySource enumerablePropertySource = sourceList.get(0);
Collection<PropertySource<?>> source2 = enumerablePropertySource.getSource();
for (PropertySource<?> source : source2) {
try {
if (source instanceof PropertiesPropertySource) {
PropertiesPropertySource propertySource = (PropertiesPropertySource) source;
Map<String, Object> properties = propertySource.getSource();
if(properties!=null){
newSource.forEach(properties::put);
}
} else {
if (source instanceof MapPropertySource) {
MapPropertySource mapPropertySource = (MapPropertySource) source;
Map<String, Object> mapSource = mapPropertySource.getSource();
if (null != mapSource && !mapSource.isEmpty()) {
mapSource.putAll(newSource);
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}