我們在基于spring開發應用的時候,一般都會將數據庫的配置放置在properties文件中.
代碼分析的時候,涉及的知識點概要:
1.namespacehandler 解析xml配置文件中的自定義命名空間
2.contextnamespacehandler 上下文相關的解析器,這邊定義了具體如何解析property-placeholder的解析器
3.beandefinitionparser 解析bean definition的接口
4.beanfactorypostprocessor 加載好bean definition后可以對其進行修改
5.propertysourcesplaceholderconfigurer 處理bean definition 中的占位符
我們先來看看具體的使用吧
property的使用
在xml文件中配置properties文件
1
2
3
4
5
6
7
8
9
10
11
12
|
<?xml version= "1.0" encoding= "utf-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/xmlschema-instance" xmlns:context= "http://www.springframework.org/schema/context" xsi:schemalocation=" http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans-4.2.xsd http: //www.springframework.org/schema/context http: //www.springframework.org/schema/context/spring-context-4.2.xsd"> <context:property-placeholder location= "classpath:foo.properties" /> </beans> |
這樣/src/main/resources/foo.properties文件就會被spring加載
如果想使用多個配置文件,可以添加order字段來進行排序
使用propertysource注解配置
spring3.1添加了@propertysource注解,方便添加property文件到環境.
1
2
3
4
5
6
7
8
|
@configuration @propertysource ( "classpath:foo.properties" ) public class propertieswithjavaconfig { @bean public static propertysourcesplaceholderconfigurer propertysourcesplaceholderconfigurer() { return new propertysourcesplaceholderconfigurer(); } } |
properties的注入與使用
1.java中使用@value注解獲取
1
2
|
@value ( "${jdbc.url}" ) private string jdbcurl; |
還可以添加一個默認值
1
2
|
@value ( "${jdbc.url:adefaulturl}" ) private string jdbcurl; |
1.在spring的xml配置文件中獲取
1
2
3
|
<bean id= "datasource" > <property name= "url" value= "${jdbc.url}" /> </bean> |
源碼解析
properties配置信息的加載
spring在啟動時會通過abstractapplicationcontext#refresh啟動容器初始化工作,期間會委托loadbeandefinitions解析xml配置文件.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
protected final void refreshbeanfactory() throws beansexception { if (hasbeanfactory()) { destroybeans(); closebeanfactory(); } try { defaultlistablebeanfactory beanfactory = createbeanfactory(); beanfactory.setserializationid(getid()); customizebeanfactory(beanfactory); loadbeandefinitions(beanfactory); synchronized ( this .beanfactorymonitor) { this .beanfactory = beanfactory; } } catch (ioexception ex) { throw new applicationcontextexception( "i/o error parsing bean definition source for " + getdisplayname(), ex); } } |
loadbeandefinitions通過層層委托,找到defaultbeandefinitiondocumentreader#parsebeandefinition解析具體的bean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
protected void parsebeandefinitions(element root, beandefinitionparserdelegate delegate) { if (delegate.isdefaultnamespace(root)) { nodelist nl = root.getchildnodes(); for ( int i = 0 ; i < nl.getlength(); i++) { node node = nl.item(i); if (node instanceof element) { element ele = (element) node; if (delegate.isdefaultnamespace(ele)) { parsedefaultelement(ele, delegate); } else { delegate.parsecustomelement(ele); } } } } else { delegate.parsecustomelement(root); } } |
這邊由于不是標準類定義,所以委托beandefinitionparserdelegate解析
通過namespacehandler查找到對應的處理器是contextnamespacehandler,再通過id找到propertyplaceholderbeandefinitionparser解析器解析
1
2
3
4
5
6
7
8
9
10
11
12
|
@override public void init() { // 這就是我們要找的解析器 registerbeandefinitionparser( "property-placeholder" , new propertyplaceholderbeandefinitionparser()); registerbeandefinitionparser( "property-override" , new propertyoverridebeandefinitionparser()); registerbeandefinitionparser( "annotation-config" , new annotationconfigbeandefinitionparser()); registerbeandefinitionparser( "component-scan" , new componentscanbeandefinitionparser()); registerbeandefinitionparser( "load-time-weaver" , new loadtimeweaverbeandefinitionparser()); registerbeandefinitionparser( "spring-configured" , new springconfiguredbeandefinitionparser()); registerbeandefinitionparser( "mbean-export" , new mbeanexportbeandefinitionparser()); registerbeandefinitionparser( "mbean-server" , new mbeanserverbeandefinitionparser()); } |
propertyplaceholderbeandefinitionparser是這一輪代碼分析的重點.
我們來看看它的父類吧.
1.beandefinitionparser
被defaultbeandefinitiondocumentreader用于解析個性化的標簽
這邊只定義了一個解析element的parse api
1
2
3
|
public interface beandefinitionparser { beandefinition parse(element element, parsercontext parsercontext); } |
2.abstractbeandefinitionparser
beandefinitionparser接口的默認抽象實現.spring的拿手好戲,這邊提供了很多方便使用的api,并使用模板方法設計模式給子類提供自定義實現的鉤子
我們來看看parse時具體的處理邏輯把: 調用鉤子parseinternal解析
- 生成bean id,使用beannamegenerator生成,或者直接讀取id屬性
- 處理name 與別名aliases
- 往容器中注冊bean
- 進行事件觸發
3.abstractsinglebeandefinitionparser
解析,定義單個beandefinition的抽象父類
在parseinternal中,解析出parentname,beanclass,source;并使用beandefinitionbuilder進行封裝
4.abstractpropertyloadingbeandefinitionparser
解析property相關的屬性,如location,properties-ref,file-encoding,order等
5.propertyplaceholderbeandefinitionparser
這邊處理的事情不多,就是設置ingore-unresolvable和system-properties-mode
properties文件的加載,bean的實例化
接下來,我們再看看這個bean是在什么時候實例化的,一般類的實例化有2種,一種是單例系統啟動就實例化;一種是非單例(或者單例懶加載)在getbean時實例化.
這邊的觸發卻是通過beanfcatorypostprocessor.
beanfactorypostprocessor是在bean實例化前,修改bean definition的,比如bean definition中的占位符就是這邊解決的,而我們現在使用的properties也是這邊解決的.
這個是通過postprocessorregistrationdelegate#invokebeanfactorypostprocessors實現的.
掃描容器中的beanfactorypostprocessor,找到了這邊需要的propertysourcesplaceholderconfigurer,并通過容器的getbean實例化
1
2
3
|
protected void invokebeanfactorypostprocessors(configurablelistablebeanfactory beanfactory) { postprocessorregistrationdelegate.invokebeanfactorypostprocessors(beanfactory, getbeanfactorypostprocessors()); } |
propertysourcesplaceholderconfigurer實例化完成后,就直接進行觸發,并加載信息
1
2
|
ordercomparator.sort(priorityorderedpostprocessors); invokebeanfactorypostprocessors(priorityorderedpostprocessors, beanfactory); |
我們再來看看propertysourcesplaceholderconfigurer的繼承體系把
1.beanfactorypostprocessor
定義一個用于修改容器中bean definition的屬性的接口.其實現類在一般類使用前先實例化,并對其他類的屬性進行修改.
這跟beanpostprocessor有明顯的區別,beanpostprocessor是修改bean實例的.
2.propertiesloadersupport
加載properties文件的抽象類.
這邊具體的加載邏輯是委托propertiesloaderutils#fillproperties實現
3.propertyresourceconfigurer
bean definition中占位符的替換就是這個抽象類實現的.
實現beanfactorypostprocessor#postprocessbeanfactory,迭代容器的中的類定義,進行修改
具體如何修改就通過鉤子processproperties交由子類實現
4.placeholderconfigurersupport
使用visitor設計模式,通過beandefinitionvisitor和stringvalueresolver更新屬性
stringvalueresolver是一個轉化string類型數據的接口,真正更新屬性的api實現竟然是在
propertyplaceholderhelper#parsestringvalue
5.propertysourcesplaceholderconfigurer
覆寫postprocessorbeanfactory api定義解析流程
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.cnblogs.com/leftthen/p/5615066.html