該系列文章是本人在學(xué)習(xí) Spring 的過程中總結(jié)下來的,里面涉及到相關(guān)源碼,可能對讀者不太友好,請結(jié)合我的源碼注釋 Spring 源碼分析 GitHub 地址 進(jìn)行閱讀
Spring 版本:5.1.14.RELEASE
開始閱讀這一系列文章之前,建議先查看《深入了解 Spring IoC(面試題)》這一篇文章
該系列其他文章請查看:《死磕 Spring 之 IoC 篇 - 文章導(dǎo)讀》
BeanDefinition 的解析階段(XML 文件)
上一篇文章《BeanDefinition 的加載階段(XML 文件)》獲取到 org.w3c.dom.Document 對象后,需要通過 DefaultBeanDefinitionDocumentReader 進(jìn)行解析,解析出 XML 文件中定義的 BeanDefinition 并進(jìn)行注冊,先來回顧一下上一篇文章中的這段代碼:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// <1> 創(chuàng)建 BeanDefinitionDocumentReader 對象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// <2> 獲取已注冊的 BeanDefinition 數(shù)量
int countBefore = getRegistry().getBeanDefinitionCount();
// <3> 創(chuàng)建 XmlReaderContext 對象(讀取 Resource 資源的上下文對象)
// <4> 根據(jù) Document、XmlReaderContext 解析出所有的 BeanDefinition 并注冊
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// <5> 計(jì)算新注冊的 BeanDefinition 數(shù)量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
本文開始分析第 4 步,BeanDefinition 的解析階段,其中 BeanDefinitionDocumentReader 只有 DefaultBeanDefinitionDocumentReader 一個默認(rèn)實(shí)現(xiàn)類
BeanDefinitionDocumentReader 接口
org.springframework.beans.factory.xml.BeanDefinitionDocumentReader ,解析 DOM document 中的 BeanDefinition 并注冊,代碼如下:
public interface BeanDefinitionDocumentReader {
/**
* Read bean definitions from the given DOM document and
* register them with the registry in the given reader context.
* @param doc the DOM document
* @param readerContext the current context of the reader
* (includes the target registry and the resource being parsed)
* @throws BeanDefinitionStoreException in case of parsing errors
*/
void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) throws BeanDefinitionStoreException;
}
DefaultBeanDefinitionDocumentReader
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader ,Spring 默認(rèn)的 BeanDefinitionDocumentReader 實(shí)現(xiàn)類,從 XML 文件中解析出 BeanDefinition 并注冊
構(gòu)造函數(shù)
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
/** bean */
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
public static final String NESTED_BEANS_ELEMENT = "beans";
public static final String ALIAS_ELEMENT = "alias";
public static final String NAME_ATTRIBUTE = "name";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String IMPORT_ELEMENT = "import";
public static final String RESOURCE_ATTRIBUTE = "resource";
public static final String PROFILE_ATTRIBUTE = "profile";
@Nullable
private XmlReaderContext readerContext;
/**
* XML 文件的 BeanDefinition 解析器
*/
@Nullable
private BeanDefinitionParserDelegate delegate;
}
上面定義了 XML 文件中常用的標(biāo)簽
1. registerBeanDefinitions 方法
registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法,根據(jù) Document、XmlReaderContext 解析出所有的 BeanDefinition 并注冊,方法如下:
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 獲得 XML Document Root Element
// 執(zhí)行注冊 BeanDefinition
doRegisterBeanDefinitions(doc.getDocumentElement());
}
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// 記錄老的 BeanDefinitionParserDelegate 對象,避免再次調(diào)用當(dāng)前方法時解析出現(xiàn)問題(默認(rèn)值可能不同)
BeanDefinitionParserDelegate parent = this.delegate;
// <1> 創(chuàng)建 BeanDefinitionParserDelegate 對象 `delegate`,并初始化默認(rèn)值
this.delegate = createDelegate(getReaderContext(), root, parent);
// <2> 檢查 <beans /> 根標(biāo)簽的命名空間是否為空,或者是 http://www./schema/beans
if (this.delegate.isDefaultNamespace(root)) {
// <2.1> 獲取 `profile` 屬性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
// <2.2> 使用分隔符切分,可能有多個 `profile`
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
// <2.3> 根據(jù) Spring Environment 進(jìn)行校驗(yàn),如果所有 `profile` 都無效,則不進(jìn)行注冊
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// <3> 解析前處理
preProcessXml(root);
// <4> 解析出 XML Document 中的 BeanDefinition 并注冊
parseBeanDefinitions(root, this.delegate);
// <5> 解析后處理
postProcessXml(root);
// 設(shè)置 delegate 回老的 BeanDefinitionParserDelegate 對象
this.delegate = parent;
}
首先獲取 XML Document 的最頂層的標(biāo)簽,也就是 <beans /> ,然后對其子標(biāo)簽進(jìn)行解析,這里的過程大致如下:
- 創(chuàng)建 BeanDefinitionParserDelegate 對象
delegate ,并初始化默認(rèn)值
- 檢查
<beans /> 根標(biāo)簽是否是默認(rèn)命名空間(xmlns 屬性,為空或者是 http://www./schema/beans ),是的話進(jìn)行校驗(yàn)
- 獲取
profile 屬性,使用分隔符切分
- 根據(jù) Spring Environment 進(jìn)行校驗(yàn),如果所有
profile 都無效,則不進(jìn)行注冊
- 解析前處理,空方法,暫時忽略
- 解析出 XML Document 中的 BeanDefinition 并注冊,調(diào)用
parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法
- 解析后處理,空方法,暫時忽略
2. parseBeanDefinitions 方法
parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法,解析 XML Document 的最頂層的標(biāo)簽,解析出 BeanDefinition 并注冊,方法如下:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// <1> 如果根節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析
if (delegate.isDefaultNamespace(root)) {
// <1.1> 遍歷所有的子節(jié)點(diǎn)
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;
// <1.2> 如果該節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
// <1.3> 如果該節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析
else {
delegate.parseCustomElement(ele);
}
}
}
}
// <2> 如果根節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析
else {
delegate.parseCustomElement(root);
}
}
解析過程大致如下:
- 如果根節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析
- 遍歷所有的子節(jié)點(diǎn)
- 如果該節(jié)點(diǎn)使用默認(rèn)命名空間,執(zhí)行默認(rèn)解析,調(diào)用
parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法
- 如果該節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析,調(diào)用
BeanDefinitionParserDelegate#parseCustomElement(Element ele) 方法
- 如果根節(jié)點(diǎn)非默認(rèn)命名空間,執(zhí)行自定義解析,調(diào)用
BeanDefinitionParserDelegate#parseCustomElement(Element ele) 方法
命名空間是什么?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www./schema/beans"
xmlns:context="http://www./schema/context"
xmlns:xsi="http://www./2001/XMLSchema-instance"
xsi:schemaLocation="http://www./schema/beans
https://www./schema/beans/spring-beans.xsd
http://www./schema/context
https://www./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>
在 XML 文件中的標(biāo)簽的 xmlns 可以定義默認(rèn)的命名空間,xmlns:context 定義 context 的命名空間,xsi:schemaLocation 定義了命名空間對應(yīng)的 XSD 文件(校驗(yàn) XML 內(nèi)容)。
上面的 <beans /> 和 <bean> 標(biāo)簽的命名空間為 http://www./schema/beans ,其中 <context:component-scan /> 標(biāo)簽的命名空間為 http://www./schema/context (不是默認(rèn)命名空間)
本文主要分析默認(rèn)命名空間的解析過程,其他命名空間(注解相關(guān))在后面進(jìn)行分析,基于 Extensible XML authoring 擴(kuò)展 Spring XML 元素會進(jìn)入這里,主要是通過 NamespaceHandler 這個接口實(shí)現(xiàn)的。例如 Spring 集成 Mybatis 項(xiàng)目中的 <mybatis:scan /> 就是擴(kuò)展 Spring XML 元素,具體實(shí)現(xiàn)在后面分析;Spring 的<context:component-scan /> 最終會通過 ComponentScanBeanDefinitionParser 進(jìn)行解析。ComponentScanBeanDefinitionParser 是將 @Component 注解標(biāo)注的類轉(zhuǎn)換成 BeanDefinition 的解析器。
3. parseDefaultElement 方法
parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法,處理默認(rèn)命名空間的節(jié)點(diǎn),方法如下:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// 解析 `<import />`
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// 解析 `<alias />`,將 name 對應(yīng)的 alias 別名進(jìn)行注冊
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 解析 `<bean />`
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 循環(huán)處理,解析 `<beans />`
doRegisterBeanDefinitions(ele);
}
}
解析下面四種標(biāo)簽:
-
<import /> 標(biāo)簽,例如這么配置:<import resource="dependency-lookup-context.xml"/> ,那么這里會獲取到對應(yīng)的 XML 文件,然后進(jìn)行相同的處理過程
-
<alias /> 標(biāo)簽,將 name 對應(yīng)的 alias 別名進(jìn)行注冊,往 AliasRegistry 注冊(BeanDefinitionRegistry 繼承了它),也就是說你可以通過別名找到對應(yīng)的 Bean
-
<bean /> 標(biāo)簽,解析成 BeanDefinition 并注冊,調(diào)用 processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法
-
<beans /> 標(biāo)簽,循環(huán)處理,和前面的步驟相同
4. processBeanDefinition 方法
processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法,將 <bean /> 標(biāo)簽解析成 BeanDefinition 并注冊,方法如下:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// <1> 解析 `<bean />` 標(biāo)簽,返回 BeanDefinitionHolder 對象(包含 BeanDefinition、beanName、aliases)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// <2> 對該標(biāo)簽進(jìn)行裝飾,一般不會,暫時忽略
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// <3> 進(jìn)行 BeanDefinition 的注冊
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// <4> 發(fā)出響應(yīng)事件,通知相關(guān)的監(jiān)聽器,已完成該 Bean 標(biāo)簽的解析
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
過程如下:
- 解析
<bean /> 標(biāo)簽,返回 BeanDefinitionHolder 對象(包含 BeanDefinition、beanName、aliases),調(diào)用 BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element ele) 方法
- 對該標(biāo)簽進(jìn)行裝飾,和上面處理自定義標(biāo)簽類似,暫時忽略
- 進(jìn)行 BeanDefinition 的注冊
- 發(fā)出響應(yīng)事件,通知相關(guān)的監(jiān)聽器,已完成該 Bean 標(biāo)簽的解析
BeanDefinitionParserDelegate
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate ,解析 XML Document 里面的 BeanDefinition
5. parseBeanDefinitionElement 方法
parseBeanDefinitionElement(Element ele) 方法,將 XML Document 里面的某個標(biāo)簽解析成 BeanDefinition,方法如下:
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// <1> 計(jì)算 BeanDefinition 的 `beanName` 名稱和 `aliases` 別名集合
// <1.1> 獲取標(biāo)簽的 `id` 和 `name` 屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// <1.2> 將 `name` 屬性全部添加至別名集合
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// <1.3> 設(shè)置 Bean 的名稱,優(yōu)先 `id` 屬性,其次 `name` 屬性
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0); // 移除出別名集合
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// <1.4> 檢查 `beanName` 的唯一性
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// <2> 解析 `<bean />` 標(biāo)簽相關(guān)屬性,構(gòu)造出一個 GenericBeanDefinition 對象
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// <3> 如果不存在 `beanName`,則根據(jù) Class 對象的名稱生成一個
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) { // 內(nèi)部 Bean
// <3.1> 生成唯一的 `beanName`
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// <3.2> 生成唯一的 beanName
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// <4> 創(chuàng)建 BeanDefinitionHolder 對象,設(shè)置 `beanName` 名稱和 `aliases` 別名集合,返回
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
過程如下:
- 計(jì)算 BeanDefinition 的
beanName 名稱和 aliases 別名集合
- 獲取標(biāo)簽的
id 和 name 屬性
- 將
name 屬性全部添加至別名集合 aliases
- 設(shè)置 Bean 的名稱
beanName ,優(yōu)先 id 屬性,其次 name 屬性
- 檢查
beanName 的唯一性
- 解析
<bean /> 標(biāo)簽相關(guān)屬性,構(gòu)造出一個 GenericBeanDefinition 對象,調(diào)用 parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) 方法
- 如果不存在
beanName ,則根據(jù) Class 對象的名稱生成一個
- 創(chuàng)建 BeanDefinitionHolder 對象,設(shè)置
beanName 名稱和 aliases 別名集合,返回
6. parseBeanDefinitionElement 重載方法
parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) 方法,解析 <bean /> 成 GenericBeanDefinition 對象,方法如下:
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// <1> 獲取 `class` 和 `parent` 屬性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// <2> 構(gòu)建一個 GenericBeanDefinition 對象 `bd`
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// <3> 解析 `<bean />` 的各種屬性并賦值
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取 description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// <4> 解析 `<bean />` 的子標(biāo)簽,生成的對象設(shè)置到 `bd` 中
// <4.1> 解析 `<meta />` 元數(shù)據(jù)標(biāo)簽
parseMetaElements(ele, bd);
// <4.2> 解析 `<lookup-method />` 標(biāo)簽
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// <4.3> 解析 `<replaced-method />` 標(biāo)簽
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// <4.4> 解析 `<constructor-arg />` 構(gòu)造函數(shù)的參數(shù)集合標(biāo)簽
parseConstructorArgElements(ele, bd);
// <4.5> 解析 `<property />` 屬性標(biāo)簽
parsePropertyElements(ele, bd);
// <4.5> 解析 `<qualifier />` 標(biāo)簽
parseQualifierElements(ele, bd);
// <5> 設(shè)置 Bean 的 `resource` 資源為 XML 文件資源
bd.setResource(this.readerContext.getResource());
// <6> 設(shè)置 Bean 的 `source` 來源為 `<bean />` 標(biāo)簽對象
bd.setSource(extractSource(ele));
return bd;
}
// ... 省略 catch 各種異常
finally {
this.parseState.pop();
}
return null;
}
過程如下:
- 獲取
class 和 parent 屬性
- 構(gòu)建一個 GenericBeanDefinition 對象
bd ,設(shè)置 parentName 和 beanClass (Class 對象)或者 className (Class 名稱)
- 解析
<bean /> 的各種屬性并賦值:scope、abstract、lazy-init、autowire、depends-on、autowire-candidate、primary、init-method、destroy-method、factory-method
- 解析
<bean /> 的子標(biāo)簽,生成的對象設(shè)置到 bd 中
- 解析
<meta /> 元數(shù)據(jù)標(biāo)簽,將 key-value 保存至 Map 中
- 解析
<lookup-method /> 標(biāo)簽,解析成 LookupOverride 對象,用于實(shí)現(xiàn) Bean 中的某個方法
- 解析
<replaced-method /> 標(biāo)簽,解析成 ReplaceOverride 對象,用于替換 Bean 中的某個方法
- 解析
<constructor-arg /> 構(gòu)造函數(shù)的參數(shù)集合標(biāo)簽,將各個參數(shù)解析出來,可根據(jù) index 屬性進(jìn)行排序
- 解析
<property /> 屬性標(biāo)簽,將各個屬性解析出來,每個屬性對應(yīng)一個 PropertyValue,添加至 bd 的 MutablePropertyValues 屬性中
- 解析
<qualifier /> 標(biāo)簽,解析出需要注入的對象 AutowireCandidateQualifier
- 設(shè)置 Bean 的
resource 資源為 XML 文件資源
- 設(shè)置 Bean 的
source 來源為 <bean /> 標(biāo)簽對象
lookup-method,會被解析成 LookupOverride 對象,replaced-method 會被解析成 ReplaceOverride 對象,這兩個標(biāo)簽不是很常用
lookup-method:例如一個在一個抽象類中定義了抽象方法,可以通過這個標(biāo)簽定義一個 Bean 實(shí)現(xiàn)這個方法,Spring 會動態(tài)改變抽象類該方法的實(shí)現(xiàn)
replace-method:通過這個標(biāo)簽定義一個 Bean,去覆蓋對應(yīng)的方法,Spring 會動態(tài)替換類的這個方法
BeanDefinitionReaderUtils
org.springframework.beans.factory.support.BeanDefinitionReaderUtils ,BeanDefinition 的解析過程中的工具類
7. registerBeanDefinition 方法
registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) ,注冊 BeanDefinition,方法如下:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// <1> 注冊 BeanDefinition
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// <2> 注冊 alias 別名
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
過程如下:
- 注冊 BeanDefinition,調(diào)用
BeanDefinitionRegistry#registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法
- 注冊 alias 別名,調(diào)用
BeanDefinitionRegistry#registerAlias(String name, String alias) 方法
這里的 BeanDefinitionRegistry 實(shí)現(xiàn)類是 DefaultListableBeanFactory,它是 Spring 底層 IoC 容器,還繼承了 SimpleAliasRegistry(AliasRegistry 實(shí)現(xiàn)類)
8. 注冊 BeanDefinition
// org.springframework.beans.factory.support.DefaultListableBeanFactory
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 校驗(yàn) beanName 與 beanDefinition 非空
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// <1> 校驗(yàn) BeanDefinition
// 這是注冊前的最后一次校驗(yàn)了,主要是對屬性 methodOverrides 進(jìn)行校驗(yàn)
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// <2> 從緩存中獲取指定 beanName 的 BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// <3> 如果已經(jīng)存在
if (existingDefinition != null) {
// 如果存在但是不允許覆蓋,拋出異常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// 覆蓋 beanDefinition 大于 被覆蓋的 beanDefinition 的 ROLE ,打印 info 日志
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
// 覆蓋 beanDefinition 與 被覆蓋的 beanDefinition 不相同,打印 debug 日志
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 其它,打印 debug 日志
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// <4> 如果未存在
else {
// 檢測創(chuàng)建 Bean 階段是否已經(jīng)開啟,如果開啟了則需要對 beanDefinitionMap 進(jìn)行并發(fā)控制
if (hasBeanCreationStarted()) {
// beanDefinitionMap 為全局變量,避免并發(fā)情況
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 添加到 BeanDefinition 到 beanDefinitionMap 中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 添加 beanName 到 beanDefinitionNames 中
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 從 manualSingletonNames 移除 beanName
removeManualSingletonName(beanName);
}
}
else {
// 添加到 BeanDefinition 到 beanDefinitionMap 中
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
// 添加 beanName 到 beanDefinitionNames 中,保證注冊順序
this.beanDefinitionNames.add(beanName);
// 從 manualSingletonNames 移除 beanName
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// <5> 重新設(shè)置 beanName 對應(yīng)的緩存
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
邏輯不復(fù)雜,主要是對 beanName 和該 BeanDefinition 對象的校驗(yàn),最終將其映射保存在 beanDefinitionMap 中(ConcurrentHashMap),key 就是 beanName
9. 注冊 alias 別名
// org.springframework.core.SimpleAliasRegistry
@Override
public void registerAlias(String name, String alias) {
// 校驗(yàn) name 、 alias
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
// name == alias 則去掉alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
// 獲取 alias 已注冊的 beanName
String registeredName = this.aliasMap.get(alias);
// 已存在
if (registeredName != null) {
// 相同,則 return ,無需重復(fù)注冊
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 不允許覆蓋,則拋出 IllegalStateException 異常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// 校驗(yàn),是否存在循環(huán)指向
checkForAliasCircle(name, alias);
// 注冊 alias
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
邏輯不復(fù)雜,將 alias 與 beanName 映射保存至 aliasMap 中(ConcurrentHashMap)
總結(jié)
解析出 XML 文件中的 BeanDefinition 并注冊的整個過程大致如下:
- 根據(jù) XSD 文件對 XML 文件進(jìn)行校驗(yàn)
- 將 XML 文件資源轉(zhuǎn)換成
org.w3c.dom.Document 對象
- 根據(jù) Document 對象解析
<beans /> 標(biāo)簽,遍歷所有的子標(biāo)簽
- 如果是子標(biāo)簽是默認(rèn)的命名空間(為空或者
http://www./schema/beans )則進(jìn)行處理,例如:<import> 、<alias /> 、<bean /> 和<beans /> ,其中 <bean /> 會被解析出一個 GenericBeanDefinition 對象,然后進(jìn)行注冊
- 否則,找到對應(yīng)的 NamespaceHandler 對象進(jìn)行解析,例如:
<context:component-scan /> 、<context:annotation-config /> 、<util:list /> ,這些非默認(rèn)命名空間的標(biāo)簽都會有對應(yīng)的 BeanDefinitionParser 解析器
至此,我們通過 XML 文件定義的 Bean 已經(jīng)轉(zhuǎn)換成了 Bean 的“前身”,也就是 BeanDefinition 對象。接下來會分析在 XML 文件中,非默認(rèn)命名空間的標(biāo)簽是如何進(jìn)行處理的。
|