“不积跬步,无以至千里”。
今天聊一聊Spring中环境变量的动态添加和填充Bean属性的时候一些带“$”符号的属性值的解析问题。
因为最近做项目的时候发现了一个有意思的问题,之前也没关注过。因为项目中使用的容器类型是GenericXmlApplicationContext,所以不会使用扫描的方式来读取bean,然后封装到BeanDefinitionMap中,而是采用读取xml配置文件的方式来加载bean,也就是使用bean标签的方式来配置bean,算是比较古老的方式,类似下面这种:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people" class="com.example.spring.xmlbeans.People">
<property name="name" value="${people.name}"/>
<property name="age" value="${people.age}"/>
</bean>
</beans>
可以发现,BeanA中有一个属性,且设置了set方法,然后赋值的时候是采用这种“$”的方式,让人很容易想到是从环境变量中读取一个people.name,然后在填充bean属性的时候从环境变量中拿出来就好了,其实不是这样的!!!因为刚开始我也是这么想的,然后当我清晰的看到spring的环境变量中确实有people.name这个key,但是在填充name属性的时候死活取不到值的时候,简直比阿根廷1:2爆冷输给沙特还更要懵逼!!!
先看我是怎么操作的:
public class MainTest {
public static void main(String[] args) throws IOException {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
// 新建一个ResourcePropertySource,读取自定义的配置文件
ResourcePropertySource resourcePropertySource = new ResourcePropertySource("beans-env.properties");
// 调用容器的Api添加PropertySource,即添加自定义的配置到Spring的上下文环境中
context.getEnvironment().getPropertySources().addLast(resourcePropertySource);
context.load("beans.xml");
context.refresh();
context.registerShutdownHook();
context.start();
}
}
public class People implements ApplicationContextAware {
private String name;
private Integer age;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println(applicationContext.getEnvironment().getProperty("People name: "+"people.name"));
System.out.println(applicationContext.getEnvironment().getProperty("People age: "+"people.age"));
}
}
beans-env.properties配置文件内容如下:
people.name=messi
people.age=34
通过断点,可以很清晰的看到当前环境变量中,确实已经添加了自定义的配置文件beans-env.properties,并且可以读取people.name这个配置项,然而当程序继续运行的时候,却发生了意料之外的BUG… …
由此可以推断出,对beanDefinitionMap中bean的属性进行填充的时候,“$”符号的解析并不是从spring的环境变量中读取的!!!
然而不久前,项目组内一个老哥给了个解决方案,在beans.xml文件中添加一个bean,问题解决了!
<bean id="propertyConifgurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>beans-env.properties</value>
</property>
<property name="fileEncoding">
<value>utf-8</value>
</property>
</bean>
所以现在有两个问题需要探索:
- 如何往Spring环境变量中添加自定义的环境变量?工作原理是什么?
- PropertyPlaceholderConfigurer这个类是怎么完成bean属性填充时“$”符号解析工作的?
首先看第一个问题,怎么添加环境变量?上面代码示例很明显了:
ResourcePropertySource resourcePropertySource = new ResourcePropertySource("beans-env.properties");
context.getEnvironment().getPropertySources().addLast(resourcePropertySource);
调用上面两行代码即可,那么容器中的这个Environment对象又是在什么时候初始化的呢?
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
其实就是在容器初始化的时候!!! 看源码:
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
未完待续…