為什么開始看spring的源碼
半路轉(zhuǎn)行寫代碼快一年半了,從開始工作就在使用spring框架,雖然會用,會搭框架,但是很多時候不懂背后的原理,比如:spring是怎樣控制事務(wù)的,springmvc是怎樣處理請求的,aop是如何實(shí)現(xiàn)的...這讓人感覺非常不踏實(shí),那就開始慢慢邊看書邊研究spring的源碼吧!!!
怎樣高效的看源碼
我的答案是帶著具體的問題去看源碼,不然非常容易陷入源碼細(xì)節(jié)中不能自拔,然后就暈了,最后你發(fā)現(xiàn)這看了半天看的是啥玩意啊.
引言
spring是一個開源的設(shè)計(jì)層面框架,解決了業(yè)務(wù)邏輯層和其他各層的松耦合問題,將面向接口的編程思想貫穿整個系統(tǒng)應(yīng)用,同時它也是java工作中必備技能之一…
由于記錄的是spring源碼分析的過程,詳細(xì)用法就不一一贅述了
核心代碼
1
2
3
4
5
|
<dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> <version> 5.0 . 2 .release</version> </dependency> |
用法
1
2
3
4
5
6
7
8
9
|
public class application { public static void main(string[] args) { classpathresource resource = new classpathresource( "bean.xml" ); //整個資源加載的切入點(diǎn)。 reader.loadbeandefinitions(resource); } } |
解密
defaultlistablebeanfactory 是 spring 注冊及加載 bean 的默認(rèn)實(shí)現(xiàn),整個spring ioc模板中它可以稱得上始祖。
跟蹤defaultlistablebeanfactory,可以發(fā)現(xiàn)如下代碼塊,該設(shè)計(jì)的目的是什么?
1
2
3
4
5
6
|
public abstractautowirecapablebeanfactory() { super (); ignoredependencyinterface(beannameaware. class ); ignoredependencyinterface(beanfactoryaware. class ); ignoredependencyinterface(beanclassloaderaware. class ); } |
舉例來說,當(dāng) a 中有屬性 b 時,那么 spring 在獲取屬性 a 時,如果發(fā)現(xiàn)屬性 b 未實(shí)例化則會自動實(shí)例化屬性 b,這也是spring中提供的一個重要特性,在某些情況下 b 不會被初始化,比如實(shí)現(xiàn)了 beannameaware 接口。
spring中是這樣介紹的:自動裝配時忽略給定的依賴接口,比如通過其他方式解析application上下文注冊依賴,類似于 beanfactory 通過 beanfactoryaware 進(jìn)行的注入或者 applicationcontext 通過 applicationcontextaware 進(jìn)行的注入。
資源管理
通過 resource 接口來實(shí)現(xiàn)對 file、url、classpath 等資源的管理,resource 負(fù)責(zé)對配置文件進(jìn)行讀取,即將配置文件封裝為 resource,然后交給 xmlbeandefinitionreader 來處理。
xml 解析
xmlbeandefinitionreader 是 spring 資源文件讀取、解析、注冊的實(shí)現(xiàn),要重點(diǎn)關(guān)注該類。
跟蹤reader.loadbeandefinitions(resource);
,我們可以見到如下核心代碼(剔除注釋和拋出異常)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public int loadbeandefinitions(encodedresource encodedresource) throws beandefinitionstoreexception { try { inputstream inputstream = encodedresource.getresource().getinputstream(); try { inputsource inputsource = new inputsource(inputstream); if (encodedresource.getencoding() != null ) { inputsource.setencoding(encodedresource.getencoding()); } return doloadbeandefinitions(inputsource, encodedresource.getresource()); } finally { inputstream.close(); } } } |
上文代碼首先對 resource 做了一次編碼操作,目的就是擔(dān)心 xml 存在編碼問題
仔細(xì)觀察inputsource inputsource = new inputsource(inputstream);
,它的包名居然是org.xml.sax,所以我們可以得出spring采用的是sax解析,使用 inputsource 來決定如何讀取 xml 文件。
最后將準(zhǔn)備的數(shù)據(jù)通過參數(shù)傳入到真正核心處理部分 doloadbeandefinitions(inputsource, encodedresource.getresource())
獲取 document
1.doloadbeandefinitions(inputsource, encodedresource.getresource());
,省略若干catch和注釋
1
2
3
4
5
6
7
|
protected int doloadbeandefinitions(inputsource inputsource, resource resource) throws beandefinitionstoreexception { try { document doc = doloaddocument(inputsource, resource); return registerbeandefinitions(doc, resource); } } |
2.doloaddocument(inputsource, resource);
1
2
3
4
|
protected document doloaddocument(inputsource inputsource, resource resource) throws exception { return this .documentloader.loaddocument(inputsource, getentityresolver(), this .errorhandler, getvalidationmodeforresource(resource), isnamespaceaware()); } |
首先通過 getvalidationmodeforresource 獲取 xml 文件的驗(yàn)證模式(dtd 或者 xsd),可以自己設(shè)置驗(yàn)證方式,默認(rèn)是開啟 validation_auto 即自動獲取驗(yàn)證模式的,通過 inputstream 讀取 xml 文件,檢查是否包含 doctype 單詞,包含的話就是 dtd,否則返回 xsd。
常見的 xml 文件驗(yàn)證模式有:
1
2
3
4
5
6
7
8
9
10
11
12
|
public class xmlvalidationmodedetector { /** * indicates that dtd validation should be used (we found a "doctype" declaration). */ public static final int validation_dtd = 2 ; /** * indicates that xsd validation should be used (found no "doctype" declaration). */ public static final int validation_xsd = 3 ; public int detectvalidationmode(inputstream inputstream) throws ioexception { } } |
在 this.documentloader.loaddocument
方法中涉及到一個 entityresolver 參數(shù)
1
2
3
|
public document loaddocument(inputsource inputsource, entityresolver entityresolver, errorhandler errorhandler, int validationmode, boolean namespaceaware) throws exception { } |
何為 entityresolver ? 官方解釋: 如果 sax 應(yīng)用程序需要實(shí)現(xiàn)自定義處理外部實(shí)體,則必須實(shí)現(xiàn)此接口,并使用 setentityresolver 方法向sax 驅(qū)動器注冊一個實(shí)例。也就是說,對于解析一個 xml,sax 首先會讀取該 xml 文檔上的聲明,根據(jù)聲明去尋找相應(yīng)的 dtd 定義,以便對文檔的進(jìn)行驗(yàn)證,默認(rèn)的尋找規(guī)則,(即:網(wǎng)絡(luò)下載,通過 xml 聲明的 dtd uri地址來下載 dtd的定義),并進(jìn)行認(rèn)證,下載的過程是一個漫長的過程,而且當(dāng)網(wǎng)絡(luò)不可用時,這里會報錯,就是因?yàn)橄鄳?yīng)的 dtd 沒找到。
entityresolver 的作用是項(xiàng)目本身就可以提供一個如何尋找 dtd 聲明的方法,即由程序來實(shí)現(xiàn)尋找 dtd 的過程,這樣就避免了通過網(wǎng)絡(luò)來尋找相應(yīng)的聲明。
3.entityresolver 接受兩個參數(shù):
1
2
|
public abstract inputsource resolveentity (string publicid,string systemid) throws saxexception, ioexception; |
3.1 定義bean.xml文件,內(nèi)容如下(xsd模式)
1
2
3
4
5
|
<?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/beans http: //www.springframework.org/schema/beans/spring-beans.xsd"> </beans> |
解析到如下兩個參數(shù):
- publicid: null
- systemid: http://www.springframework.org/schema/beans/spring-beans.xsd
3.2 定義bean.xml文件,內(nèi)容如下(dtd模式)
1
2
3
4
5
|
<?xml version= "1.0" encoding= "utf-8" ?> <!doctype beans public "-//spring//dtd bean 2.0//en" "http://www.springframework.org/dtd/spring-beans.dtd" > <beans> </beans> |
解析到如下兩個參數(shù):
- publicid: -//spring//dtd bean 2.0//en
- systemid: http://www.springframework.org/dtd/spring-beans.dtd
3.3 spring 使用 delegatingentityresolver 來解析 entityresolver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class delegatingentityresolver { @override @nullable public inputsource resolveentity(string publicid, @nullable string systemid) throws saxexception, ioexception { if (systemid != null ) { if (systemid.endswith(dtd_suffix)) { return this .dtdresolver.resolveentity(publicid, systemid); } else if (systemid.endswith(xsd_suffix)) { return this .schemaresolver.resolveentity(publicid, systemid); } } return null ; } } |
我們可以看到針對不同的模式,采用了不同的解析器
- dtd: 采用 beansdtdresolver 解析,直接截取 systemid 最后的 *.dtd(如:spring-beans.dtd),然后去當(dāng)前路徑下尋找
- xsd: 采用 pluggableschemaresolver 解析,默認(rèn)加載 meta-inf/spring.schemas 文件下與 systemid 所對應(yīng)的 xsd 文件
注冊 bean
看完解析xml校驗(yàn)后,繼續(xù)跟蹤代碼,看 spring 是如何根據(jù) document 注冊 bean 信息
1
2
3
4
5
6
7
8
9
10
11
12
|
public class xmlbeandefinitionreader { public int registerbeandefinitions(document doc, resource resource) throws beandefinitionstoreexception { // 創(chuàng)建documentreader beandefinitiondocumentreader documentreader = createbeandefinitiondocumentreader(); // 記錄統(tǒng)計(jì)前的 beandefinition 數(shù) int countbefore = getregistry().getbeandefinitioncount(); // 注冊 beandefinition documentreader.registerbeandefinitions(doc, createreadercontext(resource)); // 記錄本次加載 beandefinition 的個數(shù) return getregistry().getbeandefinitioncount() - countbefore; } } |
注冊 bean 的時候首先使用一個 beandefinitionparserdelegate 類來判斷是否是默認(rèn)命名空間,實(shí)現(xiàn)是通過判斷 namespace uri 是否和默認(rèn)的 uri 相等:
1
2
3
4
5
6
|
public class beandefinitionparserdelegate { public static final string beans_namespace_uri = "http://www.springframework.org/schema/beans" ; public boolean isdefaultnamespace( @nullable string namespaceuri) { return (!stringutils.haslength(namespaceuri) || beans_namespace_uri.equals(namespaceuri)); } } |
跟蹤 documentreader.registerbeandefinitions(doc, createreadercontext(resource));
,其中 doc 是通過前面代碼塊中 loaddocument 轉(zhuǎn)換出來的,這個方法主要目的就是提取出 root 節(jié)點(diǎn)(beans)
1
2
3
4
5
6
7
8
9
|
public class defaultbeandefinitiondocumentreader { @override public void registerbeandefinitions(document doc, xmlreadercontext readercontext) { this .readercontext = readercontext; logger.debug( "loading bean definitions" ); element root = doc.getdocumentelement(); doregisterbeandefinitions(root); } } |
跟蹤 doregisterbeandefinitions(root)
,我們將看到如下處理流程
1
2
3
4
5
6
7
8
9
10
11
|
protected void doregisterbeandefinitions(element root) { // ... string profilespec = root.getattribute(profile_attribute); // ... // 空實(shí)現(xiàn) preprocessxml(root); parsebeandefinitions(root, this .delegate); // 空實(shí)現(xiàn) postprocessxml(root); this .delegate = parent; } |
首先對 profile 解析(比較常見的玩法就是不同 profile 初始化的 bean 對象不同,實(shí)現(xiàn)多環(huán)境)
接下來的解析使用了模板方法模式,其中 preprocessxml 和 postprocessxml 都是空方法,為的就是方便之后的子類在解析前后進(jìn)行一些處理。只需要覆寫這兩個方法即可。
解析并注冊 beandefinition,該部分代碼比較簡單
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
public class defaultbeandefinitiondocumentreader { /** * 解析 root 節(jié)點(diǎn)下的其它節(jié)點(diǎn) import", "alias", "bean". * @param root節(jié)點(diǎn)名稱 */ 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); } } private void parsedefaultelement(element ele, beandefinitionparserdelegate delegate) { if (delegate.nodenameequals(ele, import_element)) { importbeandefinitionresource(ele); } else if (delegate.nodenameequals(ele, alias_element)) { processaliasregistration(ele); } else if (delegate.nodenameequals(ele, bean_element)) { processbeandefinition(ele, delegate); } else if (delegate.nodenameequals(ele, nested_beans_element)) { // recurse doregisterbeandefinitions(ele); } } /** * 處理 bean 標(biāo)簽,然后將其注冊到注冊表中去 */ protected void processbeandefinition(element ele, beandefinitionparserdelegate delegate) { beandefinitionholder bdholder = delegate.parsebeandefinitionelement(ele); if (bdholder != null ) { bdholder = delegate.decoratebeandefinitionifrequired(ele, bdholder); try { // register the final decorated instance. beandefinitionreaderutils.registerbeandefinition(bdholder, getreadercontext().getregistry()); } catch (beandefinitionstoreexception ex) { getreadercontext().error( "failed to register bean definition with name '" + bdholder.getbeanname() + "'" , ele, ex); } // send registration event. getreadercontext().firecomponentregistered( new beancomponentdefinition(bdholder)); } } } |
委托 beandefinitionparserdelegate 類的 parsebeandefinitionelement 方法進(jìn)行元素解析,返回 beandefinitionholder 類型的實(shí)例 bdholder(包含了配置文件的各個屬性class、name、id、alias等)
當(dāng)返回的 bdholder 不為空的情況下,若默認(rèn)標(biāo)簽的子節(jié)點(diǎn)存在自定義屬性,則再次對自定義標(biāo)簽進(jìn)行解析
解析完畢后,委托 beandefinitionreaderutils.registerbeandefinition();
對 bdholder 進(jìn)行注冊
發(fā)送注冊事件,告知相關(guān)監(jiān)聽 bean 已經(jīng)注冊成功了
總結(jié)
熬過幾個無人知曉的秋冬春夏,撐過去一切都會順著你想要的方向走…
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
說點(diǎn)什么
全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/chapter1
原文鏈接:http://blog.battcn.com/2018/01/09/spring/spring-1/#more