Properties配置加载(@PropertySource),额外不定的配置项单独存储到Map的一次歧路记录和正确解决思路

1. 背景

笔者的一个微服务的配置是ini文件中存储的。通过下面的方式加载。

@Data
@EqualsAndHashCode(callSuper = true)
@Component
@PropertySource(value={"file:${app.config.common.path}" , "file:${app.config.path}"} , ignoreResourceNotFound=false
		, encoding="utf-8" , name="app-config" , factory = PropertiesExSourceFactory.class)
public class AppConfig extends AppConfCommon
{	
	
	@Value("${authcenter.appkey}")
	String authCenterAppKey ;
	
	@Value("${authcenter.appsecret}")
	String authCenterAppSecret ;
	
	@Value("${sailPyAi.url}")
	String sailPyAiUrl ;
	
}

现在有一些必定数量的参数,它们都以固定的前缀开始,例如

ai.models.glm-4=xxxx
ai.models.bigwatt=xxx

想把ai.models.*的配置都收集到一起

2. 错误的尝试

此尝试,虽然失败,但里面有一些信息觉得有必要记录一下。
过程:

  1. 定义一个Map
public class AppConfig extends AppConfCommon
{	
	
	@Value("${authcenter.appkey}")
	String authCenterAppKey ;
	
	@Value("${authcenter.appsecret}")
	String authCenterAppSecret ;
	
	@Value("${sailPyAi.url}")
	String sailPyAiUrl ;
		
	@Value("${ai.models.*:}")
	Map<String, Object> aiModels ;
}

  1. 修改PropertiesExSourceFactory,在渠道.*结尾的数据时构造成一个Map返回。
	static class  PropertiesExSource  extends EnumerablePropertySource<PropertiesEx>
	{

		public PropertiesExSource(String name, PropertiesEx source) 
		{
			super(name , source) ;
		}

		@Override
		public Object getProperty(String aName)
		{
			String propValue = getSource().getProperty(aName) ;
			if(propValue == null && aName.endsWith(".*"))
			{
				// 检查键
				String name = aName.substring(0, aName.length()-1) ;
				Map<String , Object> map = CS.hashMap() ;
				PropertiesEx source= getSource() ;
				for(String propName : source.stringPropertyNames())
				{
					if(propName.startsWith(name))
						map.put(propName , source.getProperty(propName)) ;
				}
				if(!map.isEmpty())
					// 因为Spring框架对@PropertySource源,认为取得的值类型一定是String,返回map会报Map转String错误。
					// 所以此处把Map转成String格式
					return new JSONObject(map).toJSONString() ;
			}
			return propValue ;
		}
		
		@Override
		public String[] getPropertyNames()
		{
			return getSource().stringPropertyNames().toArray(XArray.sEmptyStringArray) ;
		}
		
	}
  1. 因为目标类型是Map,所以需要一个将JSON字符串转成Map的Converter
public class JsonStrToMapConverter implements Converter<String, Map<String, Object>>
{

	public JsonStrToMapConverter()
	{
	}
	
	@Override
	public Map<String, Object> convert(String source)
	{
		return XString.isEmpty(source)?CS.hashMap():new JSONObject(source).toMap() ;
	}
}

4.注册这个Converter
因为配置文件加载较早,所以用下面的方式添加Converter

public class DefaultAppRunLsn implements ApplicationListener<ApplicationEvent>
{	
	// 省略
	@Override
	public void onApplicationEvent(ApplicationEvent event)
	{
		if(event instanceof ApplicationPreparedEvent)
		{
			ConfigurableApplicationContext ctx = ((ApplicationPreparedEvent)event).getApplicationContext() ;
			ConversionService cs = ctx.getBeanFactory().getConversionService() ;
			if(cs instanceof ConverterRegistry)
			{
				ConverterRegistry reg = (ConverterRegistry)cs ;
				reg.addConverter(new JsonStrToMapConverter());
			}
			// 省略
		}
		else if(event instanceof ServletWebServerInitializedEvent)
		{
			// 省略
		}
	}
}

这么做,如果只有一个PropertySource,没有问题,但是有多个的时候,会有问题,原因就是用@Value方式注入,多个PropetySource在一个池子里了,第2步中的getSource()不一定是当前@value字段所在的那个PropertySource了。

3. 正确做法

单独在定义一个配置类。如下:

@Component
@PropertySource(value="file:${app.config.path}" , ignoreResourceNotFound=false
		, encoding="utf-8" , name="ai-models" , factory = PropertiesExSourceFactory.class)
@ConfigurationProperties(prefix = "ai")
public class AiModelsConf
{
	Properties models ;
	
	public Properties getModels()
	{
		return mConf;
	}
	public void seModels(Properties aConf)
	{
		mConf = aConf;
	}
}
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值