Spring整合阿里云ACM(应用配置管理)的思路与代码实现

本文介绍如何在Spring框架下集成阿里云ACM服务,实现应用配置的集中管理和即时应用。通过自定义PropertyPlaceholderConfigurer子类,整合本地配置、ACM配置及系统环境变量,实现配置的动态更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是ACM?

阿里云ACM是阿里云提供的一款远程应用配置参数管理的服务,通过线上配置应用配置的方式(数据库连接,redis连接等)来集中管理项目中的配置参数,达到配置集中管理和即时应用的目的(PS:我的理解)。

如何基于springframwork的框架下集成ACM达到配置集中管理的目的?下面就是我能提供给大家的一些建议。

思路

说实话,拿数据库连接举例,最开始我的实现思路是从ACM上获取应用配置后,通过载体类的形式将获取到的参数反向注入到springXML文件中的datasource配置参数中,果不其然,这种骚操作是行不通的。即使成功了,还需要注入redis等很多其它配置。然后又想到了从properties文件入手,不用spring原来的PropertyPlaceholderConfigurer注入方式,自己继承PropertyPlaceholderConfigurer来修改整个容器中预加载的配置参数,这样既不用修改现有代码通过springMVC@Value()注入的方式来获取值,还能从ACM上拿到远程数据库连接等配置,实践证明这种做法是可行的。

具体步骤

修改前的步骤:项目启动===>>PropertyPlaceholderConfigurer加载本地参数和JAVA系统参数(本项目的容器配置参数)整合===>>Spring接管应用配置参数===>>项目使用配置参数

修改后的步骤:项目启动===>>ACM获取应用配置参数===>>PropertyPlaceholderConfigurer(自实现子类)加载本地properties文件参数、ACM配置参数、JAVA系统参数(本项目的容器配置参数)整合===>>Spring接管应用配置参数===>>项目使用配置参数

因为与原先框架的流程相差仅有一个步骤的不同,所以只需要修改很少的代码就可以达到集成ACM的目的,接下来就一步步地实现我们的思路。

如何从ACM上获取应用配置参数

如果仔细阅读阿里云ACM服务技术文档,那么从ACM上获取我们的线上配置是一件很容易的事。基于我的项目后端语言使用的JAVA以及springframwork框架,不需要使用nacos spring(我的目的是从项目启动时从ACM上获取最新的应用配置参数,spring并不支持代码热部署,配置修改重启应用完全没毛病,所以选择了原生的java native sdk),比如使用JAVA NATIVE SDK来实现从线上获取应用配置参数:

private static Properties acmProperties = new Properties();
    public static void main(String[] args) {
        try {
            // 从控制台命名空间管理中拷贝对应值
            Properties properties = new Properties();
            properties.put("endpoint", "$endpoint");
            properties.put("namespace", "$namespace");
            // 通过 ECS 实例 RAM 角色访问 ACM
            // properties.put("ramRoleName", "$ramRoleName");
            properties.put("accessKey", "$accessKey");
            properties.put("secretKey", "$secretKey");
            // 如果是加密配置,则添加下面两行进行自动解密
            //properties.put("openKMSFilter", true);
            //properties.put("regionId", "$regionId");
            ConfigService.init(properties);
            // 初始化的时候,给配置添加监听,配置变更会回调通知
            ConfigService.addListener("${dataId}", "${group}", new PropertiesListener() {
                @Override
                public void innerReceive(Properties properties) {
                    // TODO Auto-generated method stub
                    acmProperties = properties;
                    System.out.println(properties);
                }
            });
        } catch (ConfigException e) {
            e.printStackTrace();
        }

PropertyPlaceholderConfigurer(自实现子类)

整个类也是整合代码的关键类,唯一类。(不然我怎么说修改的代码很少呢!嚯嚯嚯),具体操作是继承该类,重写mergeProperties()方法,然后注入到Spring中,让spring接管所有配置参数。

public class GloablePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{		    	    		
	
		protected final static String CLASS_PACKAGE_PATH = "smart/assistant/console/gateway/acmconfig/";
		protected final static String PROPERTIES_PACKAGE_PATH = "conf";
		
		@Override
		public Properties mergeProperties() 
		{
			String path = this.getClass().getResource("").getPath()+"/important.properties";
			path = path.replaceAll(CLASS_PACKAGE_PATH, PROPERTIES_PACKAGE_PATH);
			Properties props=new Properties();
            try {
				props.load(new FileInputStream(path));
            	//获取ACM调用参数
				String endpoint = props.getProperty(END_POINT);
				String namespace = props.getProperty(NAME_SPACE);
				String accessKey = props.getProperty(ACCESSKEY);
				String secretKey = props.getProperty(SECRETKEY);				
				String dataId = props.getProperty(DATA_ID);
				if(StringUtils.isNotBlank(System.getenv().get(DATA_ID)))
					dataId = System.getenv().get(DATA_ID);//区别开发/测试/生产的ACM配置组
				
				String groupId = props.getProperty(GROUP_ID);
				//异步调用
				receiveProperties(endpoint, namespace, accessKey, secretKey, dataId, groupId);
				//如果不为空一直循环判断,等待返回结果
				while(true){
					if(!acmProperties.isEmpty())
						break;
				}
				//将本地properties配置和ACM配置统一			
				for(Entry<Object, Object> entry:acmProperties.entrySet()){
					props.put(entry.getKey(), entry.getValue());
				}
				return props;
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}
	    
	    
		//////////////////////////////获取配置////////////////////////////////////////////
		
		protected final static String END_POINT = "acm.endpoint";
		protected final static String NAME_SPACE = "acm.namespace";
		protected final static String ACCESSKEY = "acm.accessKey";
		protected final static String SECRETKEY = "acm.secretKey";
		protected final static String DATA_ID = "acm.dataId";
		protected final static String GROUP_ID = "acm.groupId";
		
    	//阿里云ACM控制台配置参数
    	public static Properties acmProperties = new Properties();
	    protected static void receiveProperties(String endpoint,String namespace,
	    		String accessKey,String secretKey,String dataId,String groupId) {
	        // 从控制台命名空间管理中拷贝对应值
			Properties properties = new Properties();
			properties.put("endpoint", endpoint);
			properties.put("namespace", namespace);
			// 通过 ECS 实例 RAM 角色访问 ACM
			properties.put("accessKey", accessKey);
			properties.put("secretKey", secretKey);
			// 如果是加密配置,则添加下面两行进行自动解密
			//properties.put("openKMSFilter", true);
			//properties.put("regionId", "$regionId");
			ConfigService.init(properties);
			// 主动获取配置
			// 初始化的时候,给配置添加监听,配置变更会回调通知
			ConfigService.addListener(dataId, groupId, new PropertiesListener() {
				@Override
			    public void innerReceive(Properties properties) {
			        // 当配置更新后,通过该回调函数将最新值返回给用户。
			        // 注意回调函数中不要做阻塞操作,否则阻塞通知线程。
			    	acmProperties = properties;
			    }
			});
	    } 
  
}

上面代码中循环是因为获取ACM参数是一个异步调用,我们需要等待调用返回的结果(踩坑,为什么一直拿不到参数QAQ!!!)在ConfigService.addListener()方法中,框架会一直监听整个ACM的配置,如果有更改就会通知给应用,但是对我来说Spring重载properties容器貌似还不能做到这样,所以没有什么卵用,但如果是SpringBoot或Spring Cloud支持代码热部署,可以reload数据库连接就会很有用。

Spring接管应用配置参数

不同于PropertyPlaceholderConfigurer原来在Spring.xml中的配置,我通过获取配置文件的绝对路径来加载配置文件的配置,因为我们不在依赖spring的配置了,(如果依赖就不实现我们自己想要的结果)。

	<!-- <bean id="propertyConfigurer"
		  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
		<property name="ignoreResourceNotFound" value="true" />
		<property name="locations">
			<list>
				<value>classpath:conf/important.properties</value>
			</list>
		</property>
	</bean> -->
	 <bean id="propertyConfigurer"
          class="smart.assistant.console.gateway.acmconfig.GloablePropertyPlaceholderConfigurer">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
        <property name="ignoreResourceNotFound" value="true"/>
        <property name="localOverride" value="true"/>
    </bean>

上面的配置把原来的locations注释掉,因为我们要整合整个配置文件里的配置参数和ACM的配置参数以及System.getenv()里的环境变量参数,新的配置也列在下面了。

Properties文件配置

#ACM
acm.endpoint=你在阿里云上的acm.endpoint
acm.namespace=你在阿里云上的acm.namespace
acm.accessKey=你在阿里云上的acm.accessKey
acm.secretKey=你在阿里云上的acm.secretKey
acm.dataId=你在阿里云上的acm.dataId
acm.groupId=你在阿里云上的acm.groupId

虽然,ACM接管了我们大部分的配置参数,但是我们还是少不了properties文件,因为我们还是需要参数去连接ACM,所以配置文件的配置内容虽然很少,但是也不能缺少。

最后我们就能像没有发什么事儿一样,以前如何写代码就如何写代码,完全不用关心会出什么问题,唯一不同的是我们的配置在ACM上集中管理起来,ACM的配置会覆盖本地配置文件的配置。开发/测试/生产三套不同的配置只需要一个dataId就可以满足,真是perfect!

最后的最后吐槽一句,网上关于nacos中间件的文章太少了,所以我也是摸爬滚打了大概三天才解决了这个问题。现在终于舒服了(#^.^#)!!!!还有就是很多都是直接把别人的文章抄过来,看了很多文章,内容都差不多甚至一模一样!ε=(´ο`*)))唉。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值