继承结构图如上。在加载xml,注册bean definition之后,在实例化bean definition之前,必要的时候要用到BeanFactoryPostPRocessor。它负责把XML中有些占位符式的属性还原成真实值。意思是说,有时候,XML中<bean>的属性值不固定,会随着外界因素变化,这时候,在<bean>中配置占位符,而另外定义一个属性文件来控制<bean>的属性。比如下面是一个数据库连接的XML配置:
1 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"2 destroy-method="close" 3 p:driverClassName="${driverClassName}" 4 p:url="${url}"5 p:username="${userName}" 6 p:passWord="${password}" />
下面定义了一个jdbc.properties:
1 dbName=sampledb2 driverClassName=com.MySQL.jdbc.Driver3 url=jdbc:mysql://localhost:3306/${dbName}4 #userName=root5 #password=1234
从jdbc.properties读取属性值,然后赋值给<bean> xml中的类似${password}的占位符。这时就要用到BeanFactoryPostProcessor的实现类。在这个例子中,我们可以使用PropertyPlaceHolderConfigurer。
一个例子:
首先,一共四个文件:
1 public class Car { 2 private String name; 3 private String color; 4 private double price; 5 6 private Log log=LogFactory.getLog(Car.class); 7 8 public Car(){ 9 //name="宝马";10 log.info("调用了Car的构造函数,实例化了Car..");11 }12 13 14 public String getName() {15 。。。16 。。。
后面省略了getter和setter方法。
Main.java,简单的启动类,如下:
1 public class Main { 2 private static Log log=LogFactory.getLog(Main.class); 3 4 public static void main(String args[]){ 5 applicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/test2/*.xml"); 6 Car car = ctx.getBean("car",Car.class); 7 log.info(car.toString()); 8 } 9 10 }
car.properties,car的属性文件:
1 name=奥迪A62 color=黄色3 price=50.00
beans.xml,配置文件,细节与之前几篇的栗子不太一样,稍微详说:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 9 <context:property-placeholder10 location="classpath:com/mesopotamia/test2/car.properties"11 file-encoding="utf8"12 />13 <bean id="utf8" class="java.lang.String">14 <constructor-arg value="utf-8"></constructor-arg>15 </bean> 16 <bean id="car" class="com.mesopotamia.test2.Car"17 p:name="${name}" 18 p:color="${color}"19 p:price="${price}"/>20 </beans>
运行结果如下:
1 2015-11-22 22:08:59,155 INFO [main] (Car.java:15) - 调用了Car的构造函数,实例化了Car..2 2015-11-22 22:08:59,167 INFO [main] (Main.java:15) - 名字:奥迪A6 颜色:黄色 价格:50.0
在beans.xml中,
使用了context规则和p规则。
<context:property-placeholder下面的两个属性:寻找属性文件,并设定相应的编码方式(默认是ISO-8559-1,加载中文会乱码,所以要转码)
配置转码属性后要单独写一个13-15行的编码格式的bean(这一点稍微有点淡腾)。
占位符就如17行那样:${...}。
这样配置后,spring会自动寻找classpath:com/mesopotamia/test2/car.properties下面的相应属性,并以utf-8的格式编码后,赋值给id为car的bean definition的对应属性。
默认情况下,spring会自动启动PropertyPlaceHolderConfigurer这个工厂后处理器。
我们并没有手动注册PropertyPlaceHolderConfigurer,那么spring是如何根据context:property-placeholder就去加载PropertyPlaceHolderConfigurer了?
答案:之前有讲过META-INF文件夹里面的spring-handlers和spring-schemas文件,spring-handlers可以根据命名空间找到具体的处理类,下面是context规则的spring-handlers文件:
1 http/://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler2 3 http/://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler4 http5 6 /://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler7 http8 9 /://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
第一行告诉我们,context规则的对应处理类是org.springframework.context.config.ContextNamespaceHandler,我们现在进入这个类:
1 public class ContextNamespaceHandler extends NamespaceHandlerSupport { 2 3 public void init() { 4 registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); 5 registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); 6 registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); 7 registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); 8 registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); 9 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());10 registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());11 registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());12 }13 14 }
第四行一目了然,spring看到property-placeholder,就new了一个PropertyPlaceholderBeanDefinitionParser对象。而这个对象里面有个方法:
1 @Override2 protected Class getBeanClass(Element element) {3 return PropertyPlaceholderConfigurer.class;4 }
这样就清楚了。实际上,<context:property-placeholder下面的属性就是PropertyPlaceholderConfigurer下面的属性,下面是几个重要的属性:
locations:一个属性文件的话,location就够了,而多个属性文件则是locations,它承袭自:
PropertiesLoaderSupport(PropertyPlaceholderConfigurer的父类的父类)。类型为:Properties[]。
fileEncoding:编码格式。它也是PropertiesLoaderSupport的属性。
order:如果配置文件定义了多个PropertyPlaceholderConfigurer,order负责规定执行顺序。这个属性继承自PropertyResourceConfigurer。
placeholderPrefix:像${...}为例,"${"是占位头符。也可以定义为其他占位头符。比如如果你写成&&{...}&&,那么placeholderPrefix需要定义为&&{。
placeholderSuffix:占位尾符。这两个属性属于PropertyPlaceholderConfigurer。
一般情况下,实例化一个Bean之前是没有对属性进行赋值的,即使实例化的时候也不会进行赋值,这是相当于new了一个对象罢了。但是在配置文件中定义了<bean>的属性值的话,在实例化之前,加载了XML并形成BeanDefinition后,就已经把属性值加载了,这并不是实例化的过程,更不是使用bean对象时定义属性值的过程。
立志不坚,终不济事。
------<宋>朱熹
新闻热点
疑难解答