spring源碼分析:PropertyPlaceholderConfigurer

簡介

最近工做中須要使用zookeeper配置中心管理各系統的配置,也就是須要在項目啓動時,加載zookeeper中節點的子節點的數據(例如數據庫的地址,/config/db.properties/db.addr),並替代spring xml裏的佔位符。既然須要替代佔位符,那麼天然會想到PropertyPlaceholderConfigurer這個類,該類實現了在容器的bean初始化前,替代spring容器的BeanDefinition中的值。git

本文將對PropertyPlaceholderConfigurer源碼進行解析。程序員

爲了簡化整個分析流程,假設定義了一個bean ZookeeperUtil,須要PropertyPlaceholderConfigurer類修改beanDefinition定義,替換${zookeeper.addr}。github

 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"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5                         http://www.springframework.org/schema/beans/spring-beans.xsd">
 6     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 7  
 8         <property name="ignoreUnresolvablePlaceholders" value="true"/>
 9         <property name="locations">
10             <array>
11                 <!--不一樣容器之間的屬性不能相互訪問-->
12                 <value>classpath:config.properties</value>
13             </array>
14         </property>
15     </bean>
16  
17     <bean class="com.github.thinwonton.spring.source.analysis.ZookeeperUtil">
18         <property name="addr" value="${zookeeper.addr}"/>
19     </bean>
20 </beans>

什麼是BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor接口是spring的一個擴展點。它提供了在容器建立bean以前,對bean的定義(配置元數據)進行處理的方法。spring

BeanFactoryPostProcessor接口定義了一個抽象方法:數據庫

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

其中,beanFactory是bean工廠,裏面封裝了beanDefinition,也就是bean在xml的定義。緩存

postProcessBeanFactory 何時調用呢?併發

在spring容器初始化時,AbstractApplicationContext.class的refresh()方法會被調用,該refresh()方法以下post

 1 public void refresh() throws BeansException, IllegalStateException {  2         synchronized (this.startupShutdownMonitor) {  3             // Prepare this context for refreshing.  4             // 記錄啓動時間,設置啓動標識
 5  prepareRefresh();  6  
 7             // 建立beanFactory;解析spring配置文件;獲取bean的定義,註冊BeanDefinition
 8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  9  
10             //爲BeanFactory配置容器特性,例如類加載器、事件處理器等 
11  prepareBeanFactory(beanFactory); 12  
13             try 
14                 // 內容爲空的方法,留給子類按需覆寫
15  postProcessBeanFactory(beanFactory); 16  
17                 //在這裏調用每一個BeanFactoryPostProcessor實現類的postProcessBeanFactory
18  invokeBeanFactoryPostProcessors(beanFactory); 19  
20                 //爲BeanFactory註冊BeanPost事件處理器. 
21  registerBeanPostProcessors(beanFactory); 22  
23                 //初始化信息源,和國際化相關. 
24  initMessageSource(); 25  
26                 //初始化容器事件傳播器. 
27  initApplicationEventMulticaster(); 28  
29                 //調用子類的某些特殊Bean初始化方法 
30  onRefresh(); 31  
32                 //爲事件傳播器註冊事件監聽器. 
33  registerListeners(); 34  
35                 //初始化全部剩餘的單例Bean. 
36  finishBeanFactoryInitialization(beanFactory); 37  
38                 //初始化容器的生命週期事件處理器,併發布容器的生命週期事件 
39  finishRefresh(); 40  } 41  
42             catch (BeansException ex) { 43                 //銷燬以建立的單態Bean 
44  destroyBeans(); 45                             //取消refresh操做,重置容器的同步標識. 
46  cancelRefresh(ex); 47                            throw ex; 48  } 49  } 50     }

上面的流程中,在invokeBeanFactoryPostProcessors()被調用以前,spring容器建立了beanFactory,並在beanFactory中保存了spring配置文件中bean的定義。該定義包括了前面xml中定義的兩個bean,一個是PropertyPlaceholderConfigurer,另外一個是ZookeeperUtil。注意:是定義不是初始化後的實例。this

 1 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {  2     // Invoke BeanDefinitionRegistryPostProcessors first, if any.
 3     Set<String> processedBeans = new HashSet<String>();  4  
 5     // 忽略代碼,不影響下面分析  6  
 7     // 根據類型,從bean factory中獲取bean名稱的列表。bean names在建立bean factory這個容器的時候,已經從xml中讀取並緩存了。  8     // 在這裏是須要獲取BeanFactoryPostProcessor.class類型的bean name
 9     String[] postProcessorNames =
10             beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); 11  
12     // 篩選出哪些BeanFactoryPostProcessor的實現類實現了PriorityOrdered、Ordered接口,並把它們的bean name放到相應的列表中 13     // PriorityOrdered、Ordered以及在xml中聲明的順序,影響BeanFactoryPostProcessor的實現類被調用的順序 14     // PropertyPlaceholderConfigurer的父類PropertyResourceConfigurer實現了PriorityOrdered接口,因此它會被加入到priorityOrderedPostProcessors列表中
15  
16     List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); 17     List<String> orderedPostProcessorNames = new ArrayList<String>(); 18     List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); 19     for (String ppName : postProcessorNames) { 20         if (processedBeans.contains(ppName)) { 21             // skip - already processed in first phase above
22  } 23         else if (isTypeMatch(ppName, PriorityOrdered.class)) { 24             //這個分支很是奇怪,竟然在這裏就實例化,而後把實例化的實現類放到集合中,並與下面的分支處理方式不一樣,確定不是同一個程序員寫的
25             priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); 26  } 27         else if (isTypeMatch(ppName, Ordered.class)) { 28  orderedPostProcessorNames.add(ppName); 29  } 30         else { 31  nonOrderedPostProcessorNames.add(ppName); 32  } 33  } 34  
35     // 首先,調用實現了優先級接口的BeanFactoryPostProcessor實現類,一樣實現優先級接口的類經過getOrder()的返回值進行排序,決定調用順序
36  OrderComparator.sort(priorityOrderedPostProcessors); 37  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); 38  
39     // 而後,調用實現了Ordered接口的BeanFactoryPostProcessor實現類,一樣實現Ordered接口的類經過getOrder()的返回值進行排序,決定調用順序
40     List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); 41     for (String postProcessorName : orderedPostProcessorNames) { 42         orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class)); 43  } 44  OrderComparator.sort(orderedPostProcessors); 45  invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); 46  
47     // 最後,調用普通的的BeanFactoryPostProcessor實現類,它的順序由配置文件XML的聲明順序決定
48     List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); 49     for (String postProcessorName : nonOrderedPostProcessorNames) { 50         nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class)); 51  } 52  invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); 53 }

 

上面的註釋詳細解析了 invokeBeanFactoryPostProcessors 方法的流程,該方法將從bean factory中獲取全部實現BeanFactoryPostProcessor接口的bean名稱,並對bean name列表進行了排序,最後根據bean name實例化它們,並調用BeanFactoryPostProcessor接口的postProcessBeanFactory方法。spa

在這裏,實例化BeanFactoryPostProcessor是經過 beanFactory的getBean() 方法實現的,該方法很是複雜,不是本文的討論範疇。


PropertyPlaceholderConfigurer源碼解析

PropertyPlaceholderConfigurer,用於將properties文件中定義的屬性替換到bean定義的property佔位符。

看下它的類圖:

輸入圖片說明

  • PropertiesLoaderSupport:屬性加載幫助類,提供從properties文件中讀取配置信息的能力,該類的屬性locations指定須要加載的文件所在的路徑。

  • PropertyResourceConfigurer:屬性資源的配置類,實現了BeanFactoryPostProcessor接口,所以,在容器初始化的時候,調用的就是該類的實現方法 postProcessBeanFactory() 。在 postProcessBeanFactory()方法中,從配置文件中讀取了配置項,最後調用了它的抽象方法 processProperties(),由子類決定怎麼處理這些配置屬性。除此以外,提供了convertProperty()方法,該方法是個擴展點,其實裏面什麼都沒作,它能夠用來子類在處理這些配置信息前,對配置信息進行一些轉換,例如配置屬性的解密。

  • PropertyPlaceholderConfigurer:該類實現了父類PropertyResourceConfigurer的抽象方法processProperties()。processProperties()方法會建立PlaceholderResolvingStringValueResolver類,該類提供解析字符串的方法resolveStringValue。建立了StringValueResolver實現類後,交由它的父類PlaceholderConfigurerSupport的doProcessProperties()處理。另外,佔位符的值替換爲properties中的值的實際處理類。

  • PlaceholderConfigurerSupport:該類持有佔位符符號的前綴、後綴,並在doProcessProperties()模板方法中,對BeanDefinition實例中的佔位符進行替換。

  • BeanDefinition:在spring容器初始化時,掃描並獲取每一個bean的聲明(例如在xml中聲明、經過註解聲明等),而後組裝成BeanDefinition,它描述了一個bean實例,擁有屬性值,構造參數值和具體實現提供的其餘信息。

  • BeanDefinitionVisitor:負責訪問BeanDefinition,包括(1)從beanDefinition實例中,獲取spring約定的能夠替換的參數;(2)使用佔位符解析器解析佔位符,並從properties中獲取它對應的值,最後把值設置到BeanDefinition中。

  • PropertyPlaceholderHelper:持有佔位符的前綴、後綴、多值的分隔符,負責把佔位符的字符串去除前綴、後綴,對於字符串的替換,委託給PropertyPlaceholderConfigurerResolver類處理。

  • PropertyPlaceholderConfigurerResolver:該類委託給PropertyPlaceholderConfigurer類處理。

接下來咱們看一下時序圖,幫助理解上述的類圖和整個解析佔位符的過程。 輸入圖片說明

時序圖,配合上面的類圖看,效果更佳!

相關文章
相關標籤/搜索