Pisces的属性配置文件加载

系统中访问properties文件中定义的配置项是很常见的需求,jdk提供java.util.Properties类加载指定的配置文件(当然不一定从本地文件、任意来源的流对象也可以)。

在一个大型系统中,我们有可能需要定义多个配置文件,如果每个文件都需要声明一个Properties与之对应,那么代码会显得很啰嗦。

方法一:统一写一个加载工具类,可以,但是不见得最好。
方法二:借助spring framework中的Envioment对象和@PropertySource属性

以下是一段实例代码,定义一个ApplicationConfig类,内部封装了 envioment对象,然后通过envioment获取属性。

@Component
public class ApplicationConfig {

	@Autowired(required = false)
	private Environment environment;

     /**
	 * 获取指定key对应的string值,key不存在会抛出异常
	 * 
	 * @param key
	 * @return
	 */
	public String getString(String key) {
		String val = internalGet(key);

		if (val == null) {
			throw new RuntimeException("key:" + key + " not exist!");
		}

		return val;
	}

	/**
	 * 获取指定key对应的string值,如果key不存在则返回defaultVal
	 * 
	 * @param key
	 * @param defaultVal
	 * @return
	 */
	public String getString(String key, String defaultVal) {
		String val = internalGet(key);
		if (val == null) {
			return defaultVal;
		} else {
			return val;
		}
	}

	protected String internalGet(String key) {
		return this.environment != null ? this.environment.getProperty(key)
				: System.getProperty(key);
	}
除了getString,getInteger、getBoolean也可以由这个类统一处理,方便外部调用。

如果environment被正确注入,那么我们就可以统一从中获取“所有属性”。

什么是"所有属性"?
很多,说些常见的:

  1. 系统启动时参数,即 -Dkey=value 方式定义的;
  2. 系统属性,即System.getProperty(String key);
  3. 环境变量
  4. 容器内置的一些属性,比如springboot启动时,应用上文参数、application.properties、bootstrap.properties等,这些特定的容器也会把自身的一些属性加入到environment中
  5. spting cloud config(分布式配置管理框架)的客户端会将服务器抓取的配置放到environment中
  6. 本地属性文件,当然需要通过某种方式加载进去(别急,下文会说)

当然,还有很多属性来源,不一一列举了。

我们的重点是,有了environment,我们可以通过它读取所有需要加载的属性文件了,算是一个统一的读取入口。

那么如何方便加载配置文件呢?那就要借助@PropertySource注解了。

@Component
@PropertySource(value = "classpath:myconfig.properties", ignoreResourceNotFound = true)
public class MyConfig {

	@Value("${key1:value1}")
	private String key1;

	@Value("${key2:value2}")
	private String key2;

	public String getKey1() {
		return key1;
	}

	public String getKey2() {
		return key2;
	}
}

我们定义一个Bean:MyConfig(我们可以通过很多方式定义bean,但不管什么方式,一定要确保MyConfig可以被springIOC加载),由于它被添加了@PropertySource注解,指定了属性文件的位置,那么spring就会尝试加载这个文件,并最终添加到environment中。

ignoreResourceNotFound属性意思是,如果文件不存在,就忽略,否则会抛异常,所以如果这个文件不是必须的,就设置成true.
本人也建议设置true,按照约定大于配置的原则,我们可以在代码段处理默认值。

那么如何处理默认值呢?

一,如果通过envioment获取,可以调用String getString(String key, String defaultVal)方法,外部传入默认值;

二,通过@Value注解
@Value("${key1:value1}")意思是,spring会从environment中获取key1的值,添加到key1字段中。
需要注意的是:只要environment中存在key1即可,也就是说,key1可以通过其他方式添加,不一定需要定义在myconfig.properties中;另外,所有environment的属性就可以用这样的方式注入到任何bean的字段中。
还有一点,如果environment中不存在key1,那么麻烦了,就会出错,所以key1:后面的value1就是默认值,通过这样的方式避免出错,也定义了默认值。

加载PropertySource的spring源代码在org.springframework.context.annotation.ConfigurationClassParser类中,我贴一下代码

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null;
		}
		String[] locations = propertySource.getStringArray("value");
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		for (String location : locations) {
			try {
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
			}
			catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
				// Placeholders not resolvable or resource not found when trying to open it
				if (ignoreResourceNotFound) {
					if (logger.isInfoEnabled()) {
						logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
					}
				}
				else {
					throw ex;
				}
			}
		}
	}

大家可以明显看到,ignoreResourceNotFound属性就在catch代码块里起作用了。

最后说明下优先级:
系统启动参数>application.properties参数(如果是springboot程序)>自定义文件参数>默认值。

以下是调试代码的截图

大家可以看到 myconfig文件对应的properysource在第9个,系统参数在第5个,而springboot的application.properties定义的参数是第7个,它们优先级都比myconfig要高。

以上就是pieces框架如何加载属性文件和访问属性的介绍,如果配置项是从springcloudconfig服务端获取的话,那么就不适合用@PropertySource的方式了,这个以后有机会再详细说明。

猜你喜欢

转载自blog.csdn.net/hangwen0305/article/details/82805006