基於XmlBeanFactory加載bean分析:parseDefaultElement(一)

DefaultBeanDefinitionDocumentReader.java
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)) {
                        //加載默認的階段配置信息【程序入口】
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }

parseDefaultElement(ele, delegate)該方法會根據當前節點類型作三件事情java

一、導入importnode

例如:app

<import resource="applicationContext-mybean.xml"></import>

二、alias別名post

<bean name="myBean,wahaha,gagBean" class="com.wolf.bean.Mybean1">
    <property name="name">
        <value>鄭先生</value>
    </property>
    <property name="pwd">
        <value>666666888888</value>
    </property>
</bean>

<!--給myBean起別名-->
<alias name="myBean" alias="gagBean,gagaBean,gagagaBean"></alias>

三、bean定義ui

<bean name="myBean2" class="com.wolf.bean.Mybean1">
    <property name="name">
        <value>鄭先生2</value>
    </property>
    <property name="pwd">
        <value>2222222</value>
    </property>
</bean>

四、beans集合this

對於beans集合系統會從新走doRegisterBeanDefinitions處理流程,跟程序來源同樣。spa

 

parseDefaultElement(ele, delegate)代碼實現以下code

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if(delegate.nodeNameEquals(ele, "import")) {//導入
		this.importBeanDefinitionResource(ele);
	} else if(delegate.nodeNameEquals(ele, "alias")) {//別名
		this.processAliasRegistration(ele);
	} else if(delegate.nodeNameEquals(ele, "bean")) {//bean
		this.processBeanDefinition(ele, delegate);
	} else if(delegate.nodeNameEquals(ele, "beans")) {//bean集合
		this.doRegisterBeanDefinitions(ele);
	}

}

1、importxml

protected void importBeanDefinitionResource(Element ele) {
	String location = ele.getAttribute("resource");
	if(!StringUtils.hasText(location)) {
		this.getReaderContext().error("Resource location must not be empty", ele);
	} else {
		location = this.getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
		LinkedHashSet actualResources = new LinkedHashSet(4);
		boolean absoluteLocation = false;

		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		} catch (URISyntaxException var11) {
			;
		}

		int actResArray;
		if(absoluteLocation) {
			try {
				actResArray = this.getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
			} catch (BeanDefinitionStoreException var10) {
				this.getReaderContext().error("Failed to import bean definitions from URL location [" + location + "]", ele, var10);
			}
		} else {
			try {
				Resource relativeResource = this.getReaderContext().getResource().createRelative(location);
				//判斷當前資源文件是否存在
				if(relativeResource.exists()) {
					//執行XmlBeanDefinitionReader的loadBeanDefinitions,跟加載資源的流程一致
					//因此import資源處理操做是很是簡單的
					actResArray = this.getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				} else {
					String baseLocation = this.getReaderContext().getResource().getURL().toString();
					actResArray = this.getReaderContext().getReader().loadBeanDefinitions(StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}

			} catch (IOException var8) {
				this.getReaderContext().error("Failed to resolve current resource location", ele, var8);
			} catch (BeanDefinitionStoreException var9) {
				this.getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, var9);
			}
		}
		
		
		Resource[] actResArray1 = (Resource[])actualResources.toArray(new Resource[actualResources.size()]);
		//這裏作了一個事件通知
		this.getReaderContext().fireImportProcessed(location, actResArray1, this.extractSource(ele));
	}
}

2、alias別名解析對象

protected void processAliasRegistration(Element ele) {
	//經過當前節點的別名信息配置
	//name 表示BeanName
	String name = ele.getAttribute("name");
	//別名信息
	String alias = ele.getAttribute("alias");
	boolean valid = true;
	if(!StringUtils.hasText(name)) {
		this.getReaderContext().error("Name must not be empty", ele);
		valid = false;
	}

	if(!StringUtils.hasText(alias)) {
		this.getReaderContext().error("Alias must not be empty", ele);
		valid = false;
	}

	if(valid) {
		try {
			//XmlReaderContext-->BeanDefinitionRegistry-->GenericApplicationContext(接口BeanDefinitionRegistry)的registerAlias方法[DefaultListableBeanFactory beanFactory]
			//其實就是往DefaultListableBeanFactory beanFactory中設置別名信息(SimpleAliasRegistry爲具體別名註冊實現)
			//private final Map<String, String> aliasMap = new ConcurrentHashMap(16); 別名屬性記錄器
			this.getReaderContext().getRegistry().registerAlias(name, alias);
		} catch (Exception var6) {
			this.getReaderContext().error("Failed to register alias \'" + alias + "\' for bean with name \'" + name + "\'", ele, var6);
		}

		this.getReaderContext().fireAliasRegistered(name, alias, this.extractSource(ele));
	}

}

 

根據類圖結構能夠看到

DefaultListableBeanFactory爲SimpleAliasRegistry的子類

而後在父類實現了別名的註冊,子類直接使用便可。

--分部代碼實現
DefaultBeanDefinitionDocumentReader.java類中持有XmlReaderContext readerContext對象
其中this.getReaderContext()其實就是去獲取XmlReaderContext
protected final XmlReaderContext getReaderContext() {
	return this.readerContext;
}
當拿到XmlReaderContext對象後,經過XmlReaderContext提供的getRegistry()獲取註冊器
XmlReaderContext.java類中持有XmlBeanDefinitionReader reader xmlBean讀取器,在xmlBean讀取器中獲取註冊器
public final BeanDefinitionRegistry getRegistry() {
	return this.reader.getRegistry();
}
AbstractBeanDefinitionReader.java類中持有
private final BeanDefinitionRegistry registry//BeanDefinitionRegistry爲一個接口
public final BeanDefinitionRegistry getRegistry() {
	return this.registry;
}
GenericApplicationContext.java持有DefaultListableBeanFactory beanFactory
public void registerAlias(String beanName, String alias) {
	this.beanFactory.registerAlias(beanName, alias);
}
SimpleAliasRegistry.java
private final Map<String, String> aliasMap = new ConcurrentHashMap(16);
public void registerAlias(String name, String alias) {
	if(alias.equals(name)) {
		this.aliasMap.remove(alias);
	} else {
		String registeredName = (String)this.aliasMap.get(alias);
		if(registeredName != null) {
			if(registeredName.equals(name)) {
				return;
			}

			if(!this.allowAliasOverriding()) {
				throw new IllegalStateException("Cannot register alias \'" + alias + "\' for name \'" + name + "\': It is already registered for name \'" + registeredName + "\'.");
			}
		}

		this.checkForAliasCircle(name, alias);
		this.aliasMap.put(alias, name);
	}

}

總結:其實別名的註冊思路很簡單。就是在beanFactory容器中去查找當前beanName是否存在別名,若是存在則直接return;若是別名與beanName的名稱相同時也return;

這裏還加入了一個邏輯就是判斷別名是否可以被替換在目前的代碼實現直接返回了true,也就是說默認狀況能夠直接替換別名的。

protected boolean allowAliasOverriding() {
    return true;
}
this.checkForAliasCircle(name, alias)

這裏作了一個驗證,就是驗證當前註冊的別名是否被其餘的beanName註冊過,若是註冊過會拋出

throw new IllegalStateException("Cannot register alias \'" + alias + "\' for name \'" + name + "\': Circular reference - \'" + name + "\' is a direct or indirect alias for \'" + alias + "\' already")異常

3、beans處理

beans處理邏輯也比較簡單

直接調用了this.doRegisterBeanDefinitions(ele)邏輯,而後咱們跟蹤代碼,發現跟處理rootElement邏輯相同。

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

                return;
            }
        }
    }

    this.preProcessXml(root);
    this.parseBeanDefinitions(root, this.delegate);
    this.postProcessXml(root);
    this.delegate = parent;
}

4、bean標籤處理

TIPS:針對bean標籤的處理邏輯相對比較複雜,這裏要深刻分析。

【接】基於XmlBeanFactory加載bean分析:parseDefaultElement(二)

相關文章
相關標籤/搜索