最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2018年01月15日 10:06:53   作者:唐亞峰  
這篇文章主要給大家介紹了關于Spring解密之XML解析與Bean注冊的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面小編來一起學習學習吧。

為什么開始看spring的源碼

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

怎樣高效的看源碼

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

引言

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

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

核心代碼

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>5.0.2.RELEASE</version>
</dependency>

用法

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 的默認實現(xiàn),整個Spring Ioc模板中它可以稱得上始祖。

跟蹤DefaultListableBeanFactory,可以發(fā)現(xiàn)如下代碼塊,該設計的目的是什么?

public AbstractAutowireCapableBeanFactory() {
 super();
 ignoreDependencyInterface(BeanNameAware.class);
 ignoreDependencyInterface(BeanFactoryAware.class);
 ignoreDependencyInterface(BeanClassLoaderAware.class);
}

舉例來說,當 A 中有屬性 B 時,那么 Spring 在獲取屬性 A 時,如果發(fā)現(xiàn)屬性 B 未實例化則會自動實例化屬性 B,這也是Spring中提供的一個重要特性,在某些情況下 B 不會被初始化,比如實現(xiàn)了 BeanNameAware 接口。

Spring中是這樣介紹的:自動裝配時忽略給定的依賴接口,比如通過其他方式解析Application上下文注冊依賴,類似于 BeanFactory 通過 BeanFactoryAware 進行的注入或者 ApplicationContext 通過 ApplicationContextAware 進行的注入。

資源管理

通過 Resource 接口來實現(xiàn)對 File、URL、Classpath 等資源的管理,Resource 負責對配置文件進行讀取,即將配置文件封裝為 Resource,然后交給 XmlBeanDefinitionReader 來處理。

XML 解析

XmlBeanDefinitionReader 是 Spring 資源文件讀取、解析、注冊的實現(xiàn),要重點關注該類。

跟蹤reader.loadBeanDefinitions(resource); ,我們可以見到如下核心代碼(剔除注釋和拋出異常)

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 文件。

最后將準備的數(shù)據(jù)通過參數(shù)傳入到真正核心處理部分 doLoadBeanDefinitions(inputSource, encodedResource.getResource())

獲取 Document

1.doLoadBeanDefinitions(inputSource, encodedResource.getResource()); ,省略若干catch和注釋

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
 throws BeanDefinitionStoreException {
 try {
 Document doc = doLoadDocument(inputSource, resource);
 return registerBeanDefinitions(doc, resource);
 }
}

2.doLoadDocument(inputSource, resource);

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 文件驗證模式有:

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ù)

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
 ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
}

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

EntityResolver 的作用是項目本身就可以提供一個如何尋找 DTD 聲明的方法,即由程序來實現(xiàn)尋找 DTD 的過程,這樣就避免了通過網(wǎng)絡來尋找相應的聲明。

3.EntityResolver 接受兩個參數(shù):

public abstract InputSource resolveEntity (String publicId,String systemId)
 throws SAXException, IOException;

3.1 定義bean.xml文件,內(nèi)容如下(XSD模式)

<?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模式)

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

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校驗后,繼續(xù)跟蹤代碼,看 Spring 是如何根據(jù) Document 注冊 Bean 信息

public class XmlBeanDefinitionReader {
 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
 // 創(chuàng)建DocumentReader
 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
 // 記錄統(tǒng)計前的 BeanDefinition 數(shù)
 int countBefore = getRegistry().getBeanDefinitionCount();
 // 注冊 BeanDefinition
 documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
 // 記錄本次加載 BeanDefinition 的個數(shù)
 return getRegistry().getBeanDefinitionCount() - countBefore;
 }
}

注冊 Bean 的時候首先使用一個 BeanDefinitionParserDelegate 類來判斷是否是默認命名空間,實現(xiàn)是通過判斷 namespace uri 是否和默認的 uri 相等:

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é)點(beans)

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)  ,我們將看到如下處理流程

protected void doRegisterBeanDefinitions(Element root) {
 // ...
 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
 // ... 
 // 空實現(xiàn)
 preProcessXml(root);
 parseBeanDefinitions(root, this.delegate);
 // 空實現(xiàn)
 postProcessXml(root);
 this.delegate = parent;
}

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

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

解析并注冊 BeanDefinition,該部分代碼比較簡單

public class DefaultBeanDefinitionDocumentReader {
 /**
 * 解析 root 節(jié)點下的其它節(jié)點 import", "alias", "bean".
 * @param root節(jié)點名稱
 */
 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 不為空的情況下,若默認標簽的子節(jié)點存在自定義屬性,則再次對自定義標簽進行解析
解析完畢后,委托 BeanDefinitionReaderUtils.registerBeanDefinition();對 bdHolder 進行注冊
發(fā)送注冊事件,告知相關監(jiān)聽 Bean 已經(jīng)注冊成功了

總結

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

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

說點什么

全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter1 (本地下載

相關文章

  • elasticsearch開發(fā)中data-streams使用解析

    elasticsearch開發(fā)中data-streams使用解析

    這篇文章主要為大家介紹了elasticsearch開發(fā)中data-streams使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • 解決Idea的選擇文件后定位瞄準器"Select Opened File"的功能不見了

    解決Idea的選擇文件后定位瞄準器"Select Opened File"的功能

    使用IntelliJ IDEA時,可能會發(fā)現(xiàn)"SelectOpenedFile"功能不見了,這個功能允許用戶快速定位到當前打開文件的位置,若要找回此功能,只需在IDEA的標題欄上右鍵,然后選擇"Always Select Opened File",這樣就可以重新啟用這個便捷的功能
    2024-11-11
  • SpringBoot3.4集成Seata的詳細指南

    SpringBoot3.4集成Seata的詳細指南

    當Spring Boot 3.4與Seata 2.0相遇,分布式事務的復雜性被壓縮至一行注解,所以這篇文章小編就來和大家詳細介紹一下SpringBoot3.4集成Seata的具體步驟吧
    2025-04-04
  • Java 圖片壓縮實現(xiàn)思路及代碼

    Java 圖片壓縮實現(xiàn)思路及代碼

    本文為大家詳細介紹下圖片壓縮的具體實現(xiàn)思路及java代碼,想學習的各位可以參考下哈,希望對大家有所幫助
    2013-07-07
  • Mybatis-Plus雪花id的使用以及解析機器ID和數(shù)據(jù)標識ID實現(xiàn)

    Mybatis-Plus雪花id的使用以及解析機器ID和數(shù)據(jù)標識ID實現(xiàn)

    這篇文章主要介紹了Mybatis-Plus雪花id的使用以及解析機器ID和數(shù)據(jù)標識ID實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • 深入學習SpringCloud之SpringCloud簡介

    深入學習SpringCloud之SpringCloud簡介

    Spring Cloud是一個一站式的開發(fā)分布式系統(tǒng)的框架,為開發(fā)者提供了一系列的構建分布式系統(tǒng)的工具集,本文給大家介紹springcloud的相關知識,感興趣的朋友跟隨一起看看吧
    2021-04-04
  • 詳解java為什么不允許類多重繼承卻允許接口多重繼承

    詳解java為什么不允許類多重繼承卻允許接口多重繼承

    這篇文章主要介紹了java為什么不允許類多重繼承卻允許接口多重繼承,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-05-05
  • Java中常用時間的一些相關方法

    Java中常用時間的一些相關方法

    日期的使用多種多樣,但萬變不離其宗,下面這篇文章主要給大家介紹了關于Java中常用時間的一些相關方法,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-10-10
  • SpringBoot+RabbitMQ方式收發(fā)消息的實現(xiàn)示例

    SpringBoot+RabbitMQ方式收發(fā)消息的實現(xiàn)示例

    這篇文章主要介紹了SpringBoot+RabbitMQ方式收發(fā)消息的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • mybatis-generator生成文件覆蓋問題的解決

    mybatis-generator生成文件覆蓋問題的解決

    這篇文章主要介紹了mybatis-generator生成文件覆蓋問題的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評論

正安县| 东兴市| 南陵县| 黄骅市| 托里县| 尉犁县| 安塞县| 铜陵市| 社旗县| 桑日县| 响水县| 张家口市| 麻城市| 县级市| 阿克苏市| 株洲市| 宁城县| 新竹市| 吴堡县| 申扎县| 临泽县| 西藏| 工布江达县| 咸宁市| 扎兰屯市| 南投县| 南岸区| 商城县| 临汾市| 汉川市| 东乌| 唐山市| 濉溪县| 咸宁市| 台北市| SHOW| 瑞金市| 读书| 神池县| 阳信县| 民县|