該系列文章是本人在學習 Spring 的過程當中總結下來的,裏面涉及到相關源碼,可能對讀者不太友好,請結合個人源碼註釋 Spring 源碼分析 GitHub 地址 進行閱讀html
Spring 版本:5.1.14.RELEASEjava
開始閱讀這一系列文章以前,建議先查看《深刻了解 Spring IoC(面試題)》這一篇文章git
該系列其餘文章請查看:《死磕 Spring 之 IoC 篇 - 文章導讀》github
上一篇文章 《Bean 的「前身」》 對 BeanDefinition 進行了介紹,Bean 是根據 BeanDefinition 配置元信息對象生成的。咱們在 Spring 中一般以這兩種方式定義一個 Bean:面向資源(XML、Properties)、面向註解,那麼 Spring 是如何將這兩種方式定義的信息轉換成 BeanDefinition 對象的,接下來會先分析面向資源(XML、Properties)這種方式 Spring 是如何處理的面試
下來熟悉一段代碼:正則表達式
dependency-lookup-context.xml
:spring
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- <context:component-scan base-package="org.geekbang.thinking.in.spring.ioc.overview" /> --> <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"> <property name="id" value="1"/> <property name="name" value="小馬哥"/> </bean> </beans>
// 建立 BeanFactory 容器 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); // XML 配置文件 ClassPath 路徑 String location = "classpath:/META-INF/dependency-lookup-context.xml"; // 加載配置 int beanDefinitionsCount = reader.loadBeanDefinitions(location); System.out.println("Bean 定義加載的數量:" + beanDefinitionsCount); // 依賴查找 System.out.println(beanFactory.getBean("user"));;
這段代碼是 Spring 中編程式使用 IoC 容器,咱們能夠看到 IoC 容器的使用過程大體以下:編程
1
步建立的 BeanFactory上面的第 3
步會解析 Resource 資源,將 XML 文件中定義的 Bean 解析成 BeanDefinition 配置元信息對象,並往 BeanDefinitionRegistry 註冊中心註冊,此時並無生成對應的 Bean 對象,須要經過依賴查找獲取到 Bean。固然,咱們在實際場景中通常不會這樣使用 Spring,這些工做都會有 Spring 來完成。接下來咱們一塊兒來看看 Sping 是如何加載 XML 文件的數組
org.springframework.beans.factory.support.BeanDefinitionReader
接口的類圖以下所示:dom
總覽:
org.springframework.beans.factory.support.BeanDefinitionReader
接口,BeanDefinition 讀取器
org.springframework.beans.factory.support.AbstractBeanDefinitionReader
抽象類,提供通用的實現,具體的資源加載邏輯在由子類實現
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
,XML 文件資源解析器,解析出 BeanDefinition 配置元信息對象並註冊
org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
,Properties 文件資源解析器
org.springframework.beans.factory.support.BeanDefinitionReader
接口,BeanDefinition 讀取器,定義了加載資源的方法,代碼以下:
public interface BeanDefinitionReader { /** 返回 BeanDefinition 註冊中心 */ BeanDefinitionRegistry getRegistry(); /** 返回 Resource 資源加載器,默認爲 PathMatchingResourcePatternResolver */ @Nullable ResourceLoader getResourceLoader(); /** 返回類加載器 */ @Nullable ClassLoader getBeanClassLoader(); /** 返回 Bean 的名稱生成器,默認爲 DefaultBeanNameGenerator */ BeanNameGenerator getBeanNameGenerator(); /** 從 Resource 資源中加載 BeanDefinition 並返回數量 */ int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException; }
org.springframework.beans.factory.support.AbstractBeanDefinitionReader
抽象類,實現了 BeanDefinitionReader 和 EnvironmentCapable 接口,代碼以下:
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable { private final BeanDefinitionRegistry registry; @Nullable private ResourceLoader resourceLoader; @Nullable private ClassLoader beanClassLoader; private Environment environment; private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // Determine ResourceLoader to use. if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } } @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int count = 0; for (Resource resource : resources) { count += loadBeanDefinitions(resource); } return count; } @Override public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { // 得到 ResourceLoader 對象 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 得到 Resource 數組,由於 Pattern 模式匹配下,可能有多個 Resource 。例如說,Ant 風格的 location Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 加載 BeanDefinition 們 int count = loadBeanDefinitions(resources); if (actualResources != null) { // 添加到 actualResources 中 Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. // 得到 Resource 對象 Resource resource = resourceLoader.getResource(location); // 加載 BeanDefinition 們 int count = loadBeanDefinitions(resource); if (actualResources != null) { // 添加到 actualResources 中 actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } } @Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; for (String location : locations) { count += loadBeanDefinitions(location); } return count; } // ... 省略相關代碼 }
在實現的方法中,最終都會調用 int loadBeanDefinitions(Resource resource)
這個方法,該方法在子類中實現
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
,XML 文件資源解析器,解析出 BeanDefinition 配置元信息對象並註冊
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * 禁用驗證模式 */ public static final int VALIDATION_NONE = XmlValidationModeDetector.VALIDATION_NONE; /** * 自動獲取驗證模式 */ public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO; /** * DTD 驗證模式 */ public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD; /** * XSD 驗證模式 */ public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD; /** Constants instance for this class. */ private static final Constants constants = new Constants(XmlBeanDefinitionReader.class); /** * 驗證模式,默認爲自動模式。 */ private int validationMode = VALIDATION_AUTO; private boolean namespaceAware = false; private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class; /** * 解析過程當中異常處理器 */ private ProblemReporter problemReporter = new FailFastProblemReporter(); private ReaderEventListener eventListener = new EmptyReaderEventListener(); private SourceExtractor sourceExtractor = new NullSourceExtractor(); @Nullable private NamespaceHandlerResolver namespaceHandlerResolver; private DocumentLoader documentLoader = new DefaultDocumentLoader(); @Nullable private EntityResolver entityResolver; private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger); /** * XML 驗證模式探測器 */ private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector(); /** * 當前線程,正在加載的 EncodedResource 集合。 */ private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>( "XML bean definition resources currently being loaded"); /** * Create new XmlBeanDefinitionReader for the given bean factory. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry */ public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } }
loadBeanDefinitions(Resource resource)
方法,解析 Resource 資源的入口,方法以下:
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } // <1> 獲取當前線程正在加載的 Resource 資源集合,添加當前 Resource,防止重複加載 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { // 將當前資源加入記錄中。若是已存在,拋出異常,防止循環加載同一資源出現死循環 throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // <2> 從 Resource 資源獲取 InputStream 流對象(支持編碼) InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // <3> 【核心】執行加載 Resource 資源過程,解析出 BeanDefinition 進行註冊 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { // 關閉流 inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { // <4> 從當前線程移除當前加載的 Resource 對象 currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
將 Resource 封裝成 EncodedResource 對象,目的是讓資源對象可設置編碼
doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,執行加載 Resource 資源過程,解析出 BeanDefinition 進行註冊doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,執行加載 Resource 資源過程,解析出 BeanDefinition 進行註冊,方法以下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // <1> 獲取 XML Document 實例 Document doc = doLoadDocument(inputSource, resource); // <2> 根據 Document 實例,解析出 BeanDefinition 們並註冊,返回註冊數量 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } // 省略 catch 各類異常 }
doLoadDocument(InputSource inputSource, Resource resource)
方法,獲取 XML Document 實例registerBeanDefinitions(Document doc, Resource resource)
方法,根據 Document 實例,解析出 BeanDefinition 們並註冊,返回註冊數量doLoadDocument(InputSource inputSource, Resource resource)
方法,獲取 Resource 資源對應的 XML Document 實例,方法以下:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { // <3> 經過 DefaultDocumentLoader 根據 Resource 獲取一個 Document 對象 return this.documentLoader.loadDocument(inputSource, getEntityResolver(), // <1> 獲取 `org.xml.sax.EntityResolver` 實體解析器,ResourceEntityResolver this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); // <2> 獲取 XML 文件驗證模式,保證 XML 文件的正確性 }
org.xml.sax.EntityResolver
實體解析器,ResourceEntityResolver,根據 publicId 和 systemId 獲取對應的 DTD 或 XSD 文件,用於對 XML 文件進行驗證,這個類比較關鍵,在後續文章會講到DOCTYPE
內容則爲 DTD 模式,不然爲 XSD 模式factory
,開啓校驗factory
建立 DocumentBuilder 對象 builder
,設置 EntityResolver(第 1
步建立的)、ErrorHandler 屬性builder
對 inputSource
(Resource 資源)進行解析,返回一個 Document 對象上述過程目的就是獲取到 Resource 資源對應的 Document 對象,須要通過校驗和解析兩個過程
registerBeanDefinitions(Document doc, Resource resource)
方法,根據 Document 實例,解析出 BeanDefinition 們並註冊,返回註冊數量,方法以下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // <1> 建立 BeanDefinitionDocumentReader 對象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // <2> 獲取已註冊的 BeanDefinition 數量 int countBefore = getRegistry().getBeanDefinitionCount(); // <3> 建立 XmlReaderContext 對象(讀取 Resource 資源的上下文對象) // <4> 根據 Document、XmlReaderContext 解析出全部的 BeanDefinition 並註冊 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // <5> 計算新註冊的 BeanDefinition 數量 return getRegistry().getBeanDefinitionCount() - countBefore; }
documentReader
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
方法DTD(Document Type Definition),即文檔類型定義,爲 XML 文件的驗證機制,屬於 XML 文件中組成的一部分。DTD 是一種保證 XML 文檔格式正確的有效驗證方式,它定義了相關 XML 文檔的元素、屬性、排列方式、元素的內容類型以及元素的層次結構。其實 DTD 就至關於 XML 中的 「詞彙」和「語法」,咱們能夠經過比較 XML 文件和 DTD 文件 來看文檔是否符合規範,元素和標籤使用是否正確。
DTD 在必定的階段推進了 XML 的發展,可是它自己存在着一些缺陷:
XSD(XML Schemas Definition),即 XML Schema 語言,針對 DTD 的缺陷由 W3C 在 2001 年推出。XML Schema 自己就是一個 XML 文檔,使用的是 XML 語法,所以能夠很方便的解析 XSD 文檔。相對於 DTD,XSD 具備以下優點:
咱們在 Spring 中一般以這兩種方式定義一個 Bean:面向資源(XML、Properties)、面向註解,對於第一種方式若是定義的是一個 XML 文件,Spring 會經過 XmlBeanDefinitionReader 加載該 XML 文件,獲取該 Resource 資源的 org.w3c.dom.Document
對象,這個過程會通過校驗、解析兩個步驟