问题
公司的项目使用了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的配置
解决
if (System.getProperty("confile") != null || System.getProperty("Confpath") != null) {
annCtx = new AnnotationConfigApplicationContext(PropConfig .class);
}
ApplicationContext ac = new
那么我们主动构造一个再加进去好了,xml代码如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!-- 加载相应环境配置文件,使用自定义的配置处理类 -->
<bean id="propertyConfigurer" class="test.app.cfg.PropertyConfigurer">
<property name="location">
<value>#{ systemProperties['confile'] != null?
systemProperties['confile']:
(systemProperties['Confpath'] != null
?'file:'+systemProperties['Confpath']+'config.properties':
'classpath:config_'+systemProperties['ServerLocation']+'_'+systemProperties['ServerType']+'.properties')}
</value>
</property>
</bean>
</beans>
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获得属性值)<br/>
* 主动添加应用配置对应的 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。
从应用层面来规避框架的bug并不是最令人满意的结果,解决的成本比较低,仍有维护成本,无奈Spring的Bug jira页面打不开,被墙了?