Spring源碼系列:BeanDefinition載入(上)

繼上一篇BeanFactory的建立以後,其實就是BeanDefinition載入了。一樣也是在AbstractRefreshableApplicationContext類的refreshBeanFactory方法中完成:java

//建立默認的DefaultListableBeanFactory工廠
DefaultListableBeanFactory beanFactory = createBeanFactory();
//設置Id
beanFactory.setSerializationId(getId());
//這個方法其實就是設置了allowBeanDefinitionOverriding和allowCircularReferences兩個屬性
customizeBeanFactory(beanFactory);

//調用子類的加載bean定義方法,這裏會調用XmlWebApplicationContext子類的複寫方法
loadBeanDefinitions(beanFactory);
複製代碼

這裏的loadBeanDefinitions也是一個抽象方法,AbstractRefreshableApplicationContext類中並無給出具體的實現,二是經過子類XmlWebApplicationContext的loadBeanDefinitions完成具體實現。node

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 
throws BeansException, IOException
{
//建立XmlBeanDefinitionReader,並經過回調設置到BeanFactory中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//爲XmlBeanDefinitionReader配置Environment
beanDefinitionReader.setEnvironment(getEnvironment());
//爲XmlBeanDefinitionReader配置ResourceLoader,
//由於DefaultResourceLoader是父類,因此this能夠直接被使用
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

// 容許子類提供reader的自定義初始化,而後繼續實際加載bean定義。
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
複製代碼

initBeanDefinitionReader初始化用於加載此上下文的bean定義的bean定義讀取器;默認實現是空的。而後下面經過重載的loadBeanDefinitions來作具體的bean解析(這裏用到的是XmlBeanDefinitionReader這個解析器);使用給定的XmlBeanDefinitionReader加載bean definitions。spring

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
//遍歷xml文件
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
複製代碼

此時會將咱們的applicationContext.xml讀入(固然如何還有其餘的spring配置文件,一樣會一塊拿到路徑),以下圖所示:

而後繼續經過loadBeanDefinitions的重載方法繼續委託調用。最後交給AbstractBeanDefinitionReader的loadBeanDefinitions來完成;這個代碼比較長,拆開一步一步來講,先看下總體的:數組

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//獲取ResourceLoader資源定位器
ResourceLoader resourceLoader = getResourceLoader();
//若是定位器爲null,則拋出異常
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//是不是ResourcePatternResolver類型的定位器
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
//非ResourcePatternResolver類型的
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
複製代碼

上面的代碼中須要說明下爲何要判斷當前resourceLoader是不是ResourcePatternResolver類型的,由於ResourceLoader只是提供了對classpath前綴的支持。而ResourcePatternResolver提供了對classpath*前綴的支持。也就是說ResourceLoader提供classpath下單資源文件的載入,而ResourcePatternResolver提供多資源文件的載入。
先看下假如是ResourcePatternResolver類型的(略去了部分log代碼):微信

try {
//先獲得咱們的resources
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//解析並返回beanDefinition的數量
int loadCount = loadBeanDefinitions(resources);
//加載過程當中已經被解析過的實際的Resource的填充集合
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
複製代碼

非ResourcePatternResolver類型狀況:app

// Can only load single resources by absolute URL.
//只能經過絕對URL加載單個資源
Resource resource = resourceLoader.getResource(location);
//解析並返回beanDefinition的數量
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
return loadCount;
複製代碼

而後繼續經過重載方法loadBeanDefinitions(Resource… resources)來解析(AbstractBeanDefinitionReader類中)post

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
//初始化beanDefiniton個數
int counter = 0;
//遍歷當前資源數組
for (Resource resource : resources) {
//解析具體resource中的bean
counter += loadBeanDefinitions(resource);
}
return counter;
}
複製代碼

而後交給子類XmlBeanDefinitionReader中的loadBeanDefinitions(Resource resource)方法:ui

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
複製代碼

繼續經過重載方法loadBeanDefinitions(EncodedResource encodedResource)執行,這個方法咱們只關注最核心的代碼:this

//獲取輸入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//資源讀取inputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//委託給doLoadBeanDefinitions來完成
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
複製代碼

doLoadBeanDefinitions是XmlBeanDefinitionReader中的方法,來看核心代碼:spa

//解析成符合w3c標準的Document
Document doc = doLoadDocument(inputSource, resource);
//繼續交給registerBeanDefinitions來處理
return registerBeanDefinitions(doc, resource);
複製代碼

這個時候已經將loadBeanDefinitions換成registerBeanDefinitions了,也就是載入並註冊;registerBeanDefinitions一樣也是XmlBeanDefinitionReader中的方法:

public int registerBeanDefinitions(Document doc, Resource resource) throws
BeanDefinitionStoreException
{
//獲得documentReader用來讀取document文檔
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//註冊以前的bean個數
int countBefore = getRegistry().getBeanDefinitionCount();
//解析並註冊bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
複製代碼

仍然沒有處理,繼續交給BeanDefinitionDocumentReader的registerBeanDefinitions方法來完成:

//這個實現根據「spring-beans」XSD(或DTD)解析bean定義。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
複製代碼

仍是沒處理,又細化一步,交給DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions(Element root)方法:

protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
複製代碼

任何嵌套的<beans>元素都將致使此方法的遞歸。 爲了正確傳播和保存<beans> default- *屬性,請跟蹤當前(父)委託,該委託可能爲null。 爲了回退的目的,建立一個引用父對象的新(子)委託,而後最終重置this.delegate回到它的原始(父)引用。這個行爲模仿了一堆委託,而實際上並不須要一個委託。(翻譯的有點蹩腳,大概意思就是這)

因此說DefaultBeanDefinitionDocumentReader本身也沒幹這事,又給了BeanDefinitionParserDelegate,而後就是preProcessXml()、parseBeanDefinitions()、postProcessXml()方法;其中preProcessXml()和postProcessXml()默認是空方法,本身沒有實現。具體解析在parseBeanDefinitions(root, this.delegate)中完成。

BeanDefinitionParserDelegate用於將 Document 的內容轉成 BeanDefinition實例;BeanDefinitionDocumentReader 自己不具有該功能而是交給了該類來完成。

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);
}
}
複製代碼

這個方法就是解析文檔中根目錄下的元素:「import」,「alias」,「bean」。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//解析一個「import」元素,並將給定資源的bean定義加載到bean工廠中。
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//處理給定的別名元素,向註冊表註冊別名。
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//處理給定的bean元素,解析bean定義並將其註冊到註冊表中。
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//在給定的根<beans />元素內註冊每一個bean定義。
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
複製代碼

先來看processBeanDefinition這個方法;

BeanDefinitionHolder是一個BeanDefinition的持有者,其定義了一下變量,並對如下變量提供get和set操做。這個在後面的說道BeanDefinition體系的時候再聊。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//獲取一個BeanDefinitionHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//首先根據自定義屬性進行裝飾。
//基於自定義嵌套元素進行裝飾。
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 註冊最終裝飾的實例。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 發送註冊事件。
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
複製代碼

接着看registerBeanDefinition這個方法:經過給定的bean工廠註冊給定的bean definition 。

public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)

throws BeanDefinitionStoreException
{

// 在主名稱下注冊bean定義。
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// 若是有的話,註冊bean名稱的別名,
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
複製代碼

registerBeanDefinition裏面又經過調用BeanDefinitionRegistry接口的實現DefaultListableBeanFactory來完成具體的註冊過程。關於DefaultListableBeanFactoryregisterBeanDefinition方法的解析邏輯將在Spring源碼系列:BeanDefinition載入(下)中來講.


歡迎關注微信公衆號

相關文章
相關標籤/搜索