公司的项目使用了Spring,用的xml配置,并且需要spring加载一个property配置文件---可能大部分企业Java项目都是这样。
由于一个特殊的需求,需要spring解析的Properties对象,也需要获取一些配置的值。
配置文件的选择逻辑是这样的:如果指定了配置文件URL ,则使用此配置,否则 ,如果指定了配置文件的本地文件路径,则使用此配置,否则使用classpath内配置。
对于获取指定属性,如 "server.port" ,原来的做法是这样的:按照配置文件选择的逻辑自己读一遍properties文件去获取属性。
但问题来了:现在配置文件的选择逻辑变了,原来的自己读properties文件的代码读不到东西了。这种方式容易出错,并且系统傻傻的读了两遍配置。
于是去查看Spring文档,发现ApplicationContext有个 getEnvironment() 方法,返回Environment对象,Environment有getProperty()方法。
离目标很接近了,赶紧试试,发现悲剧了,什么也获取不到--BUG ?!;
然后测试改用Annotation方式配置:
@Configurable
@Configuration
@PropertySource("${confile: file:/${Confpath}/config.properties }")
public class PropConfig {
public @Bean PropertySourcesPlaceholderConfigurer propertyConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
}
发现注解配置方式下Environment::getProperty可以获取到属性值。但也有问题,注解的el表达式不支持嵌套
也就是说支持三元表达式 A!=null? A:B , 但不支持 A!=null?(B!=null? B:C) .也就无法再支持classpath的配置
PropertyConfigurer.java 代码如下:
public class PropertyConfigurer extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
implements ApplicationContextAware {
private Properties props;
private ApplicationContext applicationContext;
private String resourceName;
protected Properties mergeProperties() throws IOException {
Properties props = super.mergeProperties();
if (applicationContext != null) {
addPropertySource(applicationContext, props);
}
this.props = props;
return props;
}
public void setLocation(Resource location) {
super.setLocation(location);
this.resourceName = location.getDescription();
}
public void setLocations(Resource... locations) {
super.setLocations(locations);
int LEN;
if (locations != null && (LEN = locations.length) > 0) {
StringBuilder sb = new StringBuilder(LEN * 50);
sb.append("[ ").append(locations[0].getDescription());
for (int i = 1; i < LEN; i++) {
sb.append(", ").append(locations[i].getDescription());
}
sb.append(" ]");
this.resourceName = sb.toString();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
if (props != null) {
addPropertySource(applicationContext, props);
}
}
/**
* 规避Spring 的 bug(xml配置方式,无法通过 Environment获得属性值)
* 主动添加应用配置对应的 PropertySource 以解决问题
*
* @param applicationContext
* @param props
*/
private void addPropertySource(ApplicationContext applicationContext, Properties props) {
ConfigurableEnvironment env = (ConfigurableEnvironment) applicationContext.getEnvironment();
PropertySource> src = null;
String resName = resourceName != null ? resourceName : "AppProps";
try {
Constructor> con = ResourcePropertySource.class.getDeclaredConstructor(String.class, String.class,
Map.class);
con.setAccessible(true);
src = (ResourcePropertySource) con.newInstance(resName, null, props);
} catch (Exception e) {
src = new PropertiesPropertySource(resName, props);
}
if (src != null) {
// add PropertySource
env.getPropertySources().addLast(src);
}
}
}
从应用层面来规避框架的bug并不是最令人满意的结果,解决的成本比较低,仍有维护成本,无奈Spring的Bug jira页面打不开,被墙了?