激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring解密之XML解析與Bean注冊示例詳解

Spring解密之XML解析與Bean注冊示例詳解

2021-03-19 12:20唐亞峰 Java教程

這篇文章主要給大家介紹了關于Spring解密之XML解析與Bean注冊的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面小編來一起學習學習吧。

為什么開始看spring的源碼

半路轉行寫代碼快一年半了,從開始工作就在使用spring框架,雖然會用,會搭框架,但是很多時候不懂背后的原理,比如:spring是怎樣控制事務的,springmvc是怎樣處理請求的,aop是如何實現的...這讓人感覺非常不踏實,那就開始慢慢邊看書邊研究spring的源碼吧!!!

怎樣高效的看源碼

我的答案是帶著具體的問題去看源碼,不然非常容易陷入源碼細節中不能自拔,然后就暈了,最后你發現這看了半天看的是啥玩意啊.

引言

spring是一個開源的設計層面框架,解決了業務邏輯層和其他各層的松耦合問題,將面向接口的編程思想貫穿整個系統應用,同時它也是java工作中必備技能之一…

由于記錄的是spring源碼分析的過程,詳細用法就不一一贅述了

核心代碼

?
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) {
 beandefinitionregistry beanfactory = new defaultlistablebeanfactory();
 xmlbeandefinitionreader reader = new xmlbeandefinitionreader(beanfactory);
 classpathresource resource = new classpathresource("bean.xml");
 //整個資源加載的切入點。
 reader.loadbeandefinitions(resource);
 }
}

解密

defaultlistablebeanfactory 是 spring 注冊及加載 bean 的默認實現,整個spring ioc模板中它可以稱得上始祖。

跟蹤defaultlistablebeanfactory,可以發現如下代碼塊,該設計的目的是什么?

?
1
2
3
4
5
6
public abstractautowirecapablebeanfactory() {
 super();
 ignoredependencyinterface(beannameaware.class);
 ignoredependencyinterface(beanfactoryaware.class);
 ignoredependencyinterface(beanclassloaderaware.class);
}

舉例來說,當 a 中有屬性 b 時,那么 spring 在獲取屬性 a 時,如果發現屬性 b 未實例化則會自動實例化屬性 b,這也是spring中提供的一個重要特性,在某些情況下 b 不會被初始化,比如實現了 beannameaware 接口。

spring中是這樣介紹的:自動裝配時忽略給定的依賴接口,比如通過其他方式解析application上下文注冊依賴,類似于 beanfactory 通過 beanfactoryaware 進行的注入或者 applicationcontext 通過 applicationcontextaware 進行的注入。

資源管理

通過 resource 接口來實現對 file、url、classpath 等資源的管理,resource 負責對配置文件進行讀取,即將配置文件封裝為 resource,然后交給 xmlbeandefinitionreader 來處理。

Spring解密之XML解析與Bean注冊示例詳解

xml 解析

xmlbeandefinitionreader 是 spring 資源文件讀取、解析、注冊的實現,要重點關注該類。

跟蹤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 做了一次編碼操作,目的就是擔心 xml 存在編碼問題

仔細觀察inputsource inputsource = new inputsource(inputstream); ,它的包名居然是org.xml.sax,所以我們可以得出spring采用的是sax解析,使用 inputsource 來決定如何讀取 xml 文件。

最后將準備的數據通過參數傳入到真正核心處理部分 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 文件的驗證模式(dtd 或者 xsd),可以自己設置驗證方式,默認是開啟 validation_auto 即自動獲取驗證模式的,通過 inputstream 讀取 xml 文件,檢查是否包含 doctype 單詞,包含的話就是 dtd,否則返回 xsd。

常見的 xml 文件驗證模式有:

?
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 參數

?
1
2
3
public document loaddocument(inputsource inputsource, entityresolver entityresolver,
 errorhandler errorhandler, int validationmode, boolean namespaceaware) throws exception {
}

何為 entityresolver ? 官方解釋: 如果 sax 應用程序需要實現自定義處理外部實體,則必須實現此接口,并使用 setentityresolver 方法向sax 驅動器注冊一個實例。也就是說,對于解析一個 xml,sax 首先會讀取該 xml 文檔上的聲明,根據聲明去尋找相應的 dtd 定義,以便對文檔的進行驗證,默認的尋找規則,(即:網絡下載,通過 xml 聲明的 dtd uri地址來下載 dtd的定義),并進行認證,下載的過程是一個漫長的過程,而且當網絡不可用時,這里會報錯,就是因為相應的 dtd 沒找到。

entityresolver 的作用是項目本身就可以提供一個如何尋找 dtd 聲明的方法,即由程序來實現尋找 dtd 的過程,這樣就避免了通過網絡來尋找相應的聲明。

Spring解密之XML解析與Bean注冊示例詳解

3.entityresolver 接受兩個參數:

?
1
2
public abstract inputsource resolveentity (string publicid,string systemid)
 throws saxexception, ioexception;

3.1 定義bean.xml文件,內容如下(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>

解析到如下兩個參數:

  • publicid: null
  • systemid: http://www.springframework.org/schema/beans/spring-beans.xsd

3.2 定義bean.xml文件,內容如下(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>

解析到如下兩個參數:

  • 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),然后去當前路徑下尋找
  • xsd: 采用 pluggableschemaresolver 解析,默認加載 meta-inf/spring.schemas 文件下與 systemid 所對應的 xsd 文件

注冊 bean

看完解析xml校驗后,繼續跟蹤代碼,看 spring 是如何根據 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 {
 // 創建documentreader
 beandefinitiondocumentreader documentreader = createbeandefinitiondocumentreader();
 // 記錄統計前的 beandefinition 數
 int countbefore = getregistry().getbeandefinitioncount();
 // 注冊 beandefinition
 documentreader.registerbeandefinitions(doc, createreadercontext(resource));
 // 記錄本次加載 beandefinition 的個數
 return getregistry().getbeandefinitioncount() - countbefore;
 }
}

注冊 bean 的時候首先使用一個 beandefinitionparserdelegate 類來判斷是否是默認命名空間,實現是通過判斷 namespace uri 是否和默認的 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 轉換出來的,這個方法主要目的就是提取出 root 節點(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);
 // ...
 // 空實現
 preprocessxml(root);
 parsebeandefinitions(root, this.delegate);
 // 空實現
 postprocessxml(root);
 this.delegate = parent;
}

首先對 profile 解析(比較常見的玩法就是不同 profile 初始化的 bean 對象不同,實現多環境)

接下來的解析使用了模板方法模式,其中 preprocessxml 和 postprocessxml 都是空方法,為的就是方便之后的子類在解析前后進行一些處理。只需要覆寫這兩個方法即可。

解析并注冊 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 節點下的其它節點 import", "alias", "bean".
 * @param root節點名稱
 */
 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 標簽,然后將其注冊到注冊表中去
 */
 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 方法進行元素解析,返回 beandefinitionholder 類型的實例 bdholder(包含了配置文件的各個屬性class、name、id、alias等)
當返回的 bdholder 不為空的情況下,若默認標簽的子節點存在自定義屬性,則再次對自定義標簽進行解析
解析完畢后,委托 beandefinitionreaderutils.registerbeandefinition();對 bdholder 進行注冊
發送注冊事件,告知相關監聽 bean 已經注冊成功了

總結

熬過幾個無人知曉的秋冬春夏,撐過去一切都會順著你想要的方向走…

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

說點什么

全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/chapter1

原文鏈接:http://blog.battcn.com/2018/01/09/spring/spring-1/#more

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 51国产偷自视频区视频小蝌蚪 | 免费a级作爱片免费观看欧洲 | 成人在线视频黄色 | 免费一级毛片免费播放 | 欧美一级毛片一级毛片 | 一区二区三区四区免费看 | 免费毛片免费看 | 欧美日韩视频在线播放 | 国产视频在线观看免费 | av在线在线 | 天天看天天摸天天操 | 久久精品99久久久久久2456 | 欧美日韩亚洲精品一区二区三区 | 91精品国 | 成人午夜精品久久久久久久3d | 欧美毛片在线观看 | 精品亚洲va在线va天堂资源站 | chinese18 xxxx videos | 久色免费 | 久久精品一区二区三 | 国产精品视频导航 | 国产精品亚洲精品日韩已方 | 成人黄色短视频在线观看 | 国产精品视频不卡 | 欧美性黄 | 99热1 | 亚洲免费网站 | 日韩黄色av网站 | 国产乱轮视频 | 日韩专区在线 | 日本逼逼视频 | av在线一区二区三区 | 精品三级内地国产在线观看 | 91久久久久久久 | 国产免费久久久久 | 免费色片 | 国产免费高清在线 | 亚洲精品久久久久久久久久久 | 精品亚洲视频在线观看 | 成人午夜在线免费观看 | 毛片一区二区三区四区 |