spring源碼解析 2 ——解析 BeanDefinition
書接上回。
上回說了xml配置文件的讀取催什,最終獲取到一個(gè)Document對(duì)象
源碼節(jié)點(diǎn)如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {
try {
//加載并解析XML搀矫,獲取到一個(gè)Document對(duì)象隅要,使用的是SAX炒瘟,相比于DOM,SAX是一種速度更快省骂,更有效的方法通惫。它逐行掃描文檔悔政,一邊掃描一邊解析迁沫。
Document doc = doLoadDocument(inputSource, resource);
//注冊(cè)BeanDefinition:
return registerBeanDefinitions(doc, resource);
}
今天我們就來看下 registerBeanDefinitions(doc, resource) 方法的具體邏輯
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//創(chuàng)建BeanDefinitionDocumentReader以用于實(shí)際從XML文檔中讀取bean定義。默認(rèn)實(shí)現(xiàn)是DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//獲取本次讀取前擂啥,注冊(cè)器(這里指的就是這個(gè)BeanFactory)中的beanDefinition的個(gè)數(shù)
int countBefore = getRegistry().getBeanDefinitionCount();
//進(jìn)行具體的注冊(cè)動(dòng)作
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//本次讀取后注冊(cè)器中的beanDefinition中的beanDefinition的個(gè)數(shù)
return getRegistry().getBeanDefinitionCount() - countBefore;
}
創(chuàng)建了一個(gè)BeanDefinitionDocumentReader, 然后就都交給它去做了鳍徽。這里有個(gè)地方值得注意一下资锰,就是這個(gè)createReaderContext(resource)方法。
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
這里的this是指XmlBeanDefinitionReader對(duì)象阶祭。
getNamespaceHandlerResolver()方法可以看一下
/**
* 命名空間處理器解決器, 名字直譯過來有點(diǎn)拗口. 這東西是干什么的呢?
* 首先搞清楚xml中的命名空間是什么, 其實(shí)就是xml文件開頭那些 xmlns, 如下:
* <?xml version="1.0" encoding="UTF-8"?>
* <beans xmlns="http://www.springframework.org/schema/beans"
* xmlns:context="http://www.springframework.org/schema/context"
* xmlns:tx="http://www.springframework.org/schema/tx"
* xmlns:p="http://www.springframework.org/schema/p"
* 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"
* >
* 則在下面xml文件中使用到的時(shí)候, 可加上前綴來表示該標(biāo)簽名是來自那個(gè)命名空間的, 可以理解為java中的包的概念,這樣在同一個(gè)xml文件中, 假設(shè)標(biāo)簽屬性名稱一樣, 可根據(jù)其所屬命名空間來區(qū)別它們
* 使用如下,這個(gè)是命令空間 xmlns:p="http://www.springframework.org/schema/p"
* <bean id="student2" class="com.qys.base.Student" p:age="19" p:id="2" p:name="小紅"></bean>
* 對(duì)xml的命名空間的理解,大家可以自己網(wǎng)上查一下
*
* 所以NamespaceHandlerResolver的主要作用就是根據(jù)傳入的命名空間的uri來獲取對(duì)應(yīng)的處理器.
*
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
DefaultNamespaceHandlerResolver是干嘛的呢绷杜,看下面的代碼
/**
* Locate the {@link NamespaceHandler} for the supplied namespace URI
* from the configured mappings.
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler}, or {@code null} if none found
* 根據(jù)傳入的命名空間的uri來獲取對(duì)應(yīng)的處理器
*/
@Override
public NamespaceHandler resolve(String namespaceUri) {
//獲取處理器映射, 首次會(huì)去讀取META-INF/spring.handlers配置文件
//主要,首次讀取配置文件的時(shí)候, 只是存了個(gè)字符串, 還不是真正的java對(duì)象
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
//根據(jù)字符串(該字符串是個(gè)類的全限定類名), 反射創(chuàng)建處理器對(duì)象
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
//調(diào)用處理器的初始化方法
namespaceHandler.init();
//將對(duì)象放入映射器中, 下次再使用到就無需再創(chuàng)建對(duì)象了
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
...
...
}
}
這東西再后面的解析自定義標(biāo)簽會(huì)用到,這里有個(gè)印象就好胖翰,就是會(huì)去讀取META-INF/spring.handlers配置文件
讓我們接著看回documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法:
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*
* 此實(shí)現(xiàn)根據(jù)“ spring-beans” XSD(或DTD接剩,歷史上)來解析bean定義。
* 打開DOM文檔萨咳;然后初始化在<beans>級(jí)別指定的默認(rèn)設(shè)置懊缺;然后解析包含的bean定義。
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
/**
* <beans>是可以嵌套的培他,任何嵌套的<beans>元素都將導(dǎo)致此方法中的遞歸(具體的遞歸在后面的parseBeanDefinitions方法中)
* 為了保證<beans>的default-*的屬性可以正確傳播和保留
* 創(chuàng)建一個(gè)新的(子)委托時(shí)鹃两,并帶有對(duì)父參考的引用以進(jìn)行回退,然后最終將this.delegate重置回其原始(父)參考舀凛。
* 所謂的嵌套俊扳,如下:
* <beans default-init-method="init" default-destroy-method="destroy">
* <beans>
* <bean id="teacher" class="com.xxx.xx.Teacher">
* <property name="id" value="xxx"/>
* <property name="name" value="xxx"/>
* <property name="subject" value="xxx"/>
* </bean>
* </beans>
* </beans>
*/
BeanDefinitionParserDelegate parent = this.delegate;
//創(chuàng)建委托類
this.delegate = createDelegate(getReaderContext(), root, parent);
//判斷該節(jié)點(diǎn)元素的命名空間是否是默認(rèn)的命名空間, 這里指的是 http://www.springframework.org/schema/beans 命名空間
if (this.delegate.isDefaultNamespace(root)) {
//獲取 profile屬性值
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
//<beans profile="dev,sit"> 可以用, ; 空格 來定義多個(gè)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.
//判斷環(huán)境中是否有這個(gè)profile,若沒有猛遍,則該<beans>下的bean就不去做解析
//傳入的profiles中馋记,只要一個(gè)匹配就返回true
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;
}
}
}
//擴(kuò)展點(diǎn),此處為空實(shí)現(xiàn)
preProcessXml(root);
//開始真正的解析xml元素
parseBeanDefinitions(root, this.delegate);
//擴(kuò)展點(diǎn),此處為空實(shí)現(xiàn)
postProcessXml(root);
this.delegate = parent;
}
首次進(jìn)來BeanDefinitionParserDelegate parent這個(gè)是null的, 后面如果有<beans>標(biāo)簽出現(xiàn)的話懊烤,會(huì)進(jìn)行遞歸調(diào)用梯醒,到時(shí)候就不是null了。這里先看一下this.delegate = createDelegate(getReaderContext(), root, parent)方法腌紧。
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//初始化默認(rèn)值
delegate.initDefaults(root, parentDelegate);
return delegate;
}
public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {
//填充默認(rèn)值茸习,該方法中,會(huì)將父委托類的默認(rèn)屬性值傳遞個(gè)當(dāng)前委托類
populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
//觸發(fā)默認(rèn)注冊(cè)事件: 一個(gè)拓展壁肋,運(yùn)行在默認(rèn)實(shí)行注冊(cè)后做一些特殊處理号胚,此處為一個(gè)空實(shí)現(xiàn)籽慢,什么也沒做
this.readerContext.fireDefaultsRegistered(this.defaults);
}
populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root)方法會(huì)去解析<beans>標(biāo)簽的各種默認(rèn)屬性。如:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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"
default-init-method="init"
default-lazy-init="true"
default-autowire="byName">
這里我們就看一個(gè)就好:
/**
* default-init-method="init" --- 默認(rèn)bean的初始化方法猫胁,優(yōu)先級(jí)低于<bean>的init-method
* default-destroy-method="destroy" --- 默認(rèn)bean的初始化方法箱亿,優(yōu)先級(jí)低于<bean>的destroy-method
* 若某個(gè)bean的Class中沒有init、destroy方法杜漠,則不會(huì)調(diào)用
* default-autowire --- 開啟bean的所有屬性自動(dòng)注入并且指定bean的屬性注入方式极景,優(yōu)先級(jí)低于<bean>的autowire
* 參考文檔 https://blog.csdn.net/otengyue/article/details/51509000
*
* default-autowire-candidates -- 優(yōu)先級(jí)沒有低于<bean>的autowire-candidate
* 參考文檔 https://blog.csdn.net/Bof_jangle/article/details/50914075
* https://blog.csdn.net/likun557/article/details/104438417
* default-merge="true" --- 子類無需再次定義父類定義過的屬性,可直接沿用,優(yōu)先級(jí)低于merge
*/
protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) {
String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
//isDefaultValue方法, 判斷自身是否設(shè)置了該屬性,或者設(shè)置成默認(rèn)值. 若是,則使用父類的屬性
if (isDefaultValue(lazyInit)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
// 可能從外部<beans>繼承驾茴,否則為false。
lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
}
defaults.setLazyInit(lazyInit);
...
...
}
這里說的從外部<beans>繼承是什么意思呢氢卡?場(chǎng)景如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-init-method="init"
<beans>
<!--若下面也設(shè)置為id="kakaxi1"的話, 則會(huì)覆蓋上面那個(gè)-->
<bean id="kakaxi2" class="com.qys.base.Teacher">
<property name="id" value="1"/>
<property name="age" value="28"/>
<property name="name" value="卡卡西2"/>
</bean>
</beans>
</beans>
讓我們回到doRegisterBeanDefinitions(Element root)方法接著往下看锈至, 上面都有注釋了,直接看parseBeanDefinitions(root, this.delegate)方法:
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
* 解析根元素的子元素, 即<beans>下的 <bean>译秦、<import>峡捡、<alias> 元素
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//判斷一下該root元素是否為默認(rèn)的命名空間下, 即 http://www.springframework.org/schema/beans , 因?yàn)橛锌赡苁亲远x的標(biāo)簽
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;
//判斷字標(biāo)簽是否在默認(rèn)命名空間下, 因?yàn)槠鋵?shí)<beans>下還可以有其他命名空間的標(biāo)簽, 比如:
// <aop:config>、<context:component-scan base-package="xxx"/>
if (delegate.isDefaultNamespace(ele)) {
//解析默認(rèn)標(biāo)簽(命名空間是http://www.springframework.org/schema/beans的)
parseDefaultElement(ele, delegate);
}
else {
//解析其他命名空間的標(biāo)簽
//<context:component-scan base-package="xxx"/>
//<tx:advice transaction-manager="xxx"/>
delegate.parseCustomElement(ele);
}
}
}
}
else {
//解析自定義標(biāo)簽
delegate.parseCustomElement(root);
}
}
自定義標(biāo)簽咱們先不看筑悴,先看默認(rèn)命名空間下的標(biāo)簽们拙,進(jìn)入parseDefaultElement(ele, delegate)方法:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//處理<import>標(biāo)簽
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//處理<alias>標(biāo)簽
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//處理<bean>標(biāo)簽
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//處理<beans>元素, 因?yàn)?lt;beans>元素可以嵌套的
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
//若是嵌套的<beans>標(biāo)簽,則遞歸處理
doRegisterBeanDefinitions(ele);
}
}
這里咱們先看最復(fù)雜的<bean>標(biāo)簽的解析阁吝,進(jìn)入processBeanDefinition(ele, delegate)方法
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
* 解析<bean>標(biāo)簽,
* 記住,這個(gè) delegate--包含--> XmlReaderContext --包含--> XmlBeanDefinitionReader --包含--> BeanDefinitionRegistry--XmlBeanFactory實(shí)現(xiàn)(或者其他工廠)
* 也就是說, 這個(gè)元素解析完后,就可以注冊(cè)到工廠中了
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//BeanDefinitionHolder 一個(gè)持有 解析后BeanDefinition砚婆、beanName、bean的aliases 的東西
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//如果有必要的話,對(duì)BeanDefinition進(jìn)行裝飾
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//getReaderContext().getRegistry() 返回的就是當(dāng)前 beanFactory
//將最后得到的BeanDefinition進(jìn)行注冊(cè), 這樣,beanFactory就持有了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.
//注冊(cè)完畢后, 發(fā)送注冊(cè)事件
//這里是一個(gè)拓展點(diǎn),當(dāng)開發(fā)人員需要對(duì)注冊(cè)BeanDefinition事件進(jìn)行監(jiān)聽時(shí),可以通過注冊(cè)監(jiān)聽器的方式并將處理邏輯寫入監(jiān)聽器中.
//目前spring中沒有對(duì)此事件做任何邏輯處理
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
這個(gè)方法:
1突勇、解析元素装盯,封裝成一個(gè)BeanDefinitionHolder對(duì)象。
2甲馋、注冊(cè)beanDefinition對(duì)象
先看如何解析封裝的:
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
* 解析<bean>元素埂奈,獲取BeanDefinition對(duì)象
*/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
//name屬性可以多個(gè), 用逗號(hào)、分號(hào)定躏、空格分隔
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果 id屬性值為空且name屬性不為空, 則取name屬性的第一個(gè)值為beanName, 否則去id屬性值為beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
//將別名集合中的第一個(gè)別名移出集合, 并賦值給beanName
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//若是嵌入bean則不會(huì)校驗(yàn)這個(gè)beanName的唯一性的,只有非嵌入才會(huì)校驗(yàn)
if (containingBean == null) {
//校驗(yàn)beanName在當(dāng)前標(biāo)簽層級(jí)層級(jí)的唯一性, 不能是別人已經(jīng)使用了的, 也不能是別名中已經(jīng)使用了的
//如若嵌入的bean,其名字是允許重復(fù)的, 非嵌入不允許重復(fù)
checkNameUniqueness(beanName, aliases, ele);
}
//解析<bean>標(biāo)簽里的其他屬性或者子標(biāo)簽, 返回一個(gè)BeanDefinition對(duì)象
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
//處理id跟name屬性都沒設(shè)置值的情況
try {
//如果beanName不存在, 那么根據(jù)spring中提供的命名規(guī)則為當(dāng)前bean生產(chǎn)對(duì)應(yīng)的beanName
//這里會(huì)區(qū)分是否為嵌入的bean, 即該<bean>是嵌入在另一個(gè)<bean>中的, 如:
// <bean id="class1" class="com.qys.base.SchoolClass">
// <property name="id" value="1"/>
// <property name="grade" value="2"/>
// <property name="classNo" value="1"/>
// <property name="teacher">
// <bean class="com.qys.base.Teacher">
// <property name="id" value="1"/>
// <property name="age" value="20"/>
// <property name="name" value="伊魯卡"/>
// </bean>
// </property>
// </bean>
if (containingBean != null) {
//處理嵌入的bean
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//處理非嵌入的bean, 調(diào)用DefaultBeanNameGenerator生成一個(gè)beanName
//如果安裝默認(rèn)的配置, 最后還是會(huì)走到
// BeanDefinitionReaderUtils.generateBeanName方法, 只是isInnerBean入?yún)⑹莊alse
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.
//如果生成器返回了類名加后綴账磺,那么如果仍然可能,為普通bean類名注冊(cè)一個(gè)別名痊远。
//這是Spring1.2/2.0向后兼容性的預(yù)期結(jié)果垮抗。
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
如果想看比較細(xì)節(jié)的話,可以看下checkNameUniqueness(beanName, aliases, ele)方法:
/**
* Validate that the specified bean name and aliases have not been used already
* within the current level of beans element nesting.
* 驗(yàn)證指定的bean名稱和別名是否尚未在當(dāng)前級(jí)別的bean元素嵌套中使用拗引。
* 當(dāng)前級(jí)別的bean元素, 也就是說,如果是在 當(dāng)前不同級(jí)別的,就不會(huì)有問題.
* 場(chǎng)景1:
* <bean id="kakaxi1" class="com.qys.base.Teacher">
* <property name="id" value="1"/>
* <property name="age" value="28"/>
* <property name="name" value="卡卡西1"/>
* </bean>
* <beans>
* <bean id="kakaxi1" class="com.qys.base.Teacher">
* <property name="id" value="1"/>
* <property name="age" value="28"/>
* <property name="name" value="卡卡西2"/>
* </bean>
* </beans>
* 以上設(shè)置就不會(huì)報(bào)錯(cuò), 若將 <beans></beans>去掉, 則會(huì)報(bào)錯(cuò)
* 以上的設(shè)置, 卡卡西2會(huì)覆蓋卡卡西1, 也就是說根據(jù)名字獲取bean的時(shí)候, 返回的是卡卡西2
* 場(chǎng)景2:
* <bean id="kakaxi1" class="com.qys.base.Teacher">
* <property name="id" value="1"/>
* <property name="age" value="28"/>
* <property name="name" value="卡卡西1"/>
* </bean>
* <bean id="class1" class="com.qys.base.SchoolClass">
* <property name="id" value="1"/>
* <property name="grade" value="2"/>
* <property name="classNo" value="1"/>
* <property name="teacher">
* <bean id="kakaxi1" class="com.qys.base.Teacher">
* <property name="id" value="1"/>
* <property name="age" value="20"/>
* <property name="name" value="卡卡西1"/>
* </bean>
* </property>
* </bean>
* 以上配置也不會(huì)報(bào)錯(cuò)借宵,甚至?xí)崾镜诙€(gè)kakaxi1可以刪除
*/
protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
String foundName = null;
if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
foundName = beanName;
}
if (foundName == null) {
//若aliases中的任意一個(gè),在usedNames中找到了, 就返回賦值給foundName
foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
}
if (foundName != null) {
error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
}
this.usedNames.add(beanName);
this.usedNames.addAll(aliases);
}
解析<bean>標(biāo)簽的具體邏輯在parseBeanDefinitionElement(ele, beanName, containingBean)方法里。
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
* 解析bean定義本身矾削,而不考慮名稱或別名.如果在分析bean定義期間出現(xiàn)問題可能會(huì)返回null
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//解析 class屬性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
//解析 parent 屬性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//創(chuàng)建BeanDefinition對(duì)象來封裝解析的屬性值,這里創(chuàng)建的是 GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析<bean>標(biāo)簽的屬性, 將其封裝至BeanDefinition對(duì)象中,如scope壤玫、abstract豁护、init-method等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//<bean>標(biāo)簽的description屬性, 用于生成文檔用的, 沒啥實(shí)際影響, 這里就不去管這個(gè)標(biāo)簽了
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析<bean>的子標(biāo)簽<meta>
parseMetaElements(ele, bd);
//解析<lookup-method>標(biāo)簽
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析<replaced-method>標(biāo)簽
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析<constructor-arg>標(biāo)簽
parseConstructorArgElements(ele, bd);
//解析<property>標(biāo)簽, 底層會(huì)走到跟解析<constructor-arg>標(biāo)簽一樣的方法
parsePropertyElements(ele, bd);
//解析<qualifier>標(biāo)簽, 平常我們都是使用@qualifier注解,在使用@Autowire自動(dòng)注入的時(shí)候,加上@Qualifier(“test”)可以指定注入哪個(gè)對(duì)象欲间;
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
感覺內(nèi)容有點(diǎn)多楚里,放下一篇文章吧。
文字寫得比較少猎贴,因?yàn)槎荚诖a注釋里了班缎。