(spring-第9回【IoC基礎篇】)BeanFactoryPostProcessor,實例化Bean之前的第二大利器


繼承結構圖如上。在加載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=sampledb
2 driverClassName=com.mysql.jdbc.Driver
3 url=jdbc:mysql://localhost:3306/${dbName}
4 #userName=root
5 #password=1234

從jdbc.properties讀取屬性值,然后賦值給<bean> xml中的類似${password}的占位符。這時就要用到BeanFactoryPostProcessor的實現類。在這個例子中,我們可以使用PropertyPlaceHolderConfigurer。

一個例子:

首先,一共四個文件:

Car.java:最簡單的bean類,源碼如下:

 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=奧迪A6
2 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-placeholder 10          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.ContextNamespaceHandler
2 
3 http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
4 http
5 
6 \://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
7 http
8 
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 @Override
2     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對象時定義屬性值的過程。

 

 

立志不堅,終不濟事。

      ------<宋>朱熹

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM