日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring源码分析,聊聊PropertyPlaceholderConfigurer

發(fā)布時間:2023/12/15 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring源码分析,聊聊PropertyPlaceholderConfigurer 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

簡介

最近工作中需要使用zookeeper配置中心管理各系統(tǒng)的配置,也就是需要在項(xiàng)目啟動時,加載zookeeper中節(jié)點(diǎn)的子節(jié)點(diǎn)的數(shù)據(jù)(例如數(shù)據(jù)庫的地址,/config/db.properties/db.addr),并替代spring xml里的占位符。既然需要替代占位符,那么自然會想到PropertyPlaceholderConfigurer這個類,該類實(shí)現(xiàn)了在容器的bean初始化前,替代spring容器的BeanDefinition中的值。

本文將對PropertyPlaceholderConfigurer源碼進(jìn)行解析。

為了簡化整個分析流程,假設(shè)定義了一個bean ZookeeperUtil,需要PropertyPlaceholderConfigurer類修改beanDefinition定義,替換${zookeeper.addr}。

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="ignoreUnresolvablePlaceholders" value="true"/><property name="locations"><array><!--不同容器之間的屬性不能相互訪問--><value>classpath:config.properties</value></array></property></bean><bean class="com.github.thinwonton.spring.source.analysis.ZookeeperUtil"><property name="addr" value="${zookeeper.addr}"/></bean> </beans>

什么是BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor接口是spring的一個擴(kuò)展點(diǎn)。它提供了在容器創(chuàng)建bean之前,對bean的定義(配置元數(shù)據(jù))進(jìn)行處理的方法。

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

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

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

postProcessBeanFactory 什么時候調(diào)用呢?

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

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.// 記錄啟動時間,設(shè)置啟動標(biāo)識prepareRefresh();// 創(chuàng)建beanFactory;解析spring配置文件;獲取bean的定義,注冊BeanDefinitionConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//為BeanFactory配置容器特性,例如類加載器、事件處理器等 prepareBeanFactory(beanFactory);try // 內(nèi)容為空的方法,留給子類按需覆寫postProcessBeanFactory(beanFactory);//在這里調(diào)用每個BeanFactoryPostProcessor實(shí)現(xiàn)類的postProcessBeanFactoryinvokeBeanFactoryPostProcessors(beanFactory);//為BeanFactory注冊BeanPost事件處理器. registerBeanPostProcessors(beanFactory);//初始化信息源,和國際化相關(guān). initMessageSource();//初始化容器事件傳播器. initApplicationEventMulticaster();//調(diào)用子類的某些特殊Bean初始化方法 onRefresh();//為事件傳播器注冊事件監(jiān)聽器. registerListeners();//初始化所有剩余的單例Bean. finishBeanFactoryInitialization(beanFactory);//初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件 finishRefresh();}catch (BeansException ex) {//銷毀以創(chuàng)建的單態(tài)Bean destroyBeans(); //取消refresh操作,重置容器的同步標(biāo)識. cancelRefresh(ex); throw ex; }}}

上面的流程中,在invokeBeanFactoryPostProcessors()被調(diào)用之前,spring容器創(chuàng)建了beanFactory,并在beanFactory中保存了spring配置文件中bean的定義。該定義包括了前面xml中定義的兩個bean,一個是PropertyPlaceholderConfigurer,另一個是ZookeeperUtil。注意:是定義不是初始化后的實(shí)例。

invokeBeanFactoryPostProcessors()方法,將會對所有實(shí)現(xiàn)BeanFactoryPostProcessor接口的bean初始化,并調(diào)用postProcessBeanFactory方法。下面一起看下 invokeBeanFactoryPostProcessors 方法。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {// Invoke BeanDefinitionRegistryPostProcessors first, if any.Set<String> processedBeans = new HashSet<String>();// 忽略代碼,不影響下面分析// 根據(jù)類型,從bean factory中獲取bean名稱的列表。bean names在創(chuàng)建bean factory這個容器的時候,已經(jīng)從xml中讀取并緩存了。// 在這里是需要獲取BeanFactoryPostProcessor.class類型的bean nameString[] postProcessorNames =beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);// 篩選出哪些BeanFactoryPostProcessor的實(shí)現(xiàn)類實(shí)現(xiàn)了PriorityOrdered、Ordered接口,并把它們的bean name放到相應(yīng)的列表中// PriorityOrdered、Ordered以及在xml中聲明的順序,影響B(tài)eanFactoryPostProcessor的實(shí)現(xiàn)類被調(diào)用的順序// PropertyPlaceholderConfigurer的父類PropertyResourceConfigurer實(shí)現(xiàn)了PriorityOrdered接口,所以它會被加入到priorityOrderedPostProcessors列表中List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();List<String> orderedPostProcessorNames = new ArrayList<String>();List<String> nonOrderedPostProcessorNames = new ArrayList<String>();for (String ppName : postProcessorNames) {if (processedBeans.contains(ppName)) {// skip - already processed in first phase above}else if (isTypeMatch(ppName, PriorityOrdered.class)) { //這個分支非常奇怪,居然在這里就實(shí)例化,然后把實(shí)例化的實(shí)現(xiàn)類放到集合中,并與下面的分支處理方式不同,肯定不是同一個程序員寫的priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));}else if (isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// 首先,調(diào)用實(shí)現(xiàn)了優(yōu)先級接口的BeanFactoryPostProcessor實(shí)現(xiàn)類,同樣實(shí)現(xiàn)優(yōu)先級接口的類通過getOrder()的返回值進(jìn)行排序,決定調(diào)用順序OrderComparator.sort(priorityOrderedPostProcessors);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);// 然后,調(diào)用實(shí)現(xiàn)了Ordered接口的BeanFactoryPostProcessor實(shí)現(xiàn)類,同樣實(shí)現(xiàn)Ordered接口的類通過getOrder()的返回值進(jìn)行排序,決定調(diào)用順序List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();for (String postProcessorName : orderedPostProcessorNames) {orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));}OrderComparator.sort(orderedPostProcessors);invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);// 最后,調(diào)用普通的的BeanFactoryPostProcessor實(shí)現(xiàn)類,它的順序由配置文件XML的聲明順序決定List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();for (String postProcessorName : nonOrderedPostProcessorNames) {nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));}invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); }

上面的注釋詳細(xì)解析了 invokeBeanFactoryPostProcessors 方法的流程,該方法將從bean factory中獲取所有實(shí)現(xiàn)BeanFactoryPostProcessor接口的bean名稱,并對bean name列表進(jìn)行了排序,最后根據(jù)bean name實(shí)例化它們,并調(diào)用BeanFactoryPostProcessor接口的postProcessBeanFactory方法。

在這里,實(shí)例化BeanFactoryPostProcessor是通過 beanFactory的getBean() 方法實(shí)現(xiàn)的,該方法非常復(fù)雜,不是本文的討論范疇。


PropertyPlaceholderConfigurer源碼解析

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

看下它的類圖:

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

  • PropertyResourceConfigurer:屬性資源的配置類,實(shí)現(xiàn)了BeanFactoryPostProcessor接口,因此,在容器初始化的時候,調(diào)用的就是該類的實(shí)現(xiàn)方法 postProcessBeanFactory() 。在 postProcessBeanFactory()方法中,從配置文件中讀取了配置項(xiàng),最后調(diào)用了它的抽象方法 processProperties(),由子類決定怎么處理這些配置屬性。除此之外,提供了convertProperty()方法,該方法是個擴(kuò)展點(diǎn),其實(shí)里面什么都沒做,它可以用來子類在處理這些配置信息前,對配置信息進(jìn)行一些轉(zhuǎn)換,例如配置屬性的解密。

  • PropertyPlaceholderConfigurer:該類實(shí)現(xiàn)了父類PropertyResourceConfigurer的抽象方法processProperties()。processProperties()方法會創(chuàng)建PlaceholderResolvingStringValueResolver類,該類提供解析字符串的方法resolveStringValue。創(chuàng)建了StringValueResolver實(shí)現(xiàn)類后,交由它的父類PlaceholderConfigurerSupport的doProcessProperties()處理。另外,占位符的值替換為properties中的值的實(shí)際處理類。

  • PlaceholderConfigurerSupport:該類持有占位符符號的前綴、后綴,并在doProcessProperties()模板方法中,對BeanDefinition實(shí)例中的占位符進(jìn)行替換。

  • BeanDefinition:在spring容器初始化時,掃描并獲取每個bean的聲明(例如在xml中聲明、通過注解聲明等),然后組裝成BeanDefinition,它描述了一個bean實(shí)例,擁有屬性值,構(gòu)造參數(shù)值和具體實(shí)現(xiàn)提供的其他信息。

  • BeanDefinitionVisitor:負(fù)責(zé)訪問BeanDefinition,包括(1)從beanDefinition實(shí)例中,獲取spring約定的可以替換的參數(shù);(2)使用占位符解析器解析占位符,并從properties中獲取它對應(yīng)的值,最后把值設(shè)置到BeanDefinition中。

  • PropertyPlaceholderHelper:持有占位符的前綴、后綴、多值的分隔符,負(fù)責(zé)把占位符的字符串去除前綴、后綴,對于字符串的替換,委托給PropertyPlaceholderConfigurerResolver類處理。

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

接下來我們看一下時序圖,幫助理解上述的類圖和整個解析占位符的過程。

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

轉(zhuǎn)載于:https://my.oschina.net/thinwonton/blog/1104546

總結(jié)

以上是生活随笔為你收集整理的spring源码分析,聊聊PropertyPlaceholderConfigurer的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。