spring4.2.9 java專案環境下ioc原始碼分析(五)——refresh之obtainFreshBeanFactory方法(@3預設標籤import,alias解析)
阿新 • • 發佈:2019-01-05
接上篇文章,到了具體解析的時候了,具體的分為兩種情況,一種是預設名稱空間的標籤<bean>;另一種是自定義名稱空間的標籤比如<context:xxx>,<tx:xxx>等。先看下預設的名稱空間的標籤解析。
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); } }
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //import if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //alias else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //bean else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } //beans else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
可以清晰的看到,預設的標籤這裡解析四種,import,alias,bean,beans.一個一個分析
對import標籤的解析再方法importBeanDefinitionResource中
protected void importBeanDefinitionResource(Element ele) { //獲取resource屬性 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); //為空報錯 if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } // Resolve system properties: e.g. "${user.dir}" //如果存在佔位符從系統屬性中取到,這說到過很多次了 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<Resource>(4); // Discover whether the location is an absolute or relative URI boolean absoluteLocation = false; try { //判斷是不是絕對路徑,帶字首的classpath:和classpath*:,file:http://解析為絕對路徑 //其他為相對路徑,瞎寫報錯 absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { // cannot convert to an URI, considering the location relative // unless it is the well-known Spring prefix "classpath*:" } // Absolute or relative? //絕對路徑 if (absoluteLocation) { try { //再次根據資源去解析 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]"); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]", ele, ex); } } else { // No URL -> considering resource location as relative to the current file. //相對路徑先去找到絕對路徑再去解析 try { int importCount; Resource relativeResource = getReaderContext().getResource().createRelative(location); //比如這裡相對路徑寫錯,檔案不存在 if (relativeResource.exists()) { importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]"); } } catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location", ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, ex); } } Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); }
雖然程式碼不少但是基本邏輯很簡單,就是找到resource的屬性,找到其對應的絕對路徑,然後再次進行解析。看了程式碼是不是很熟悉,我們一般是怎麼用的?
絕對路徑這樣寫:
<import resource="classpath:springmvc.xml"/>
<import resource="classpath*:springmvc.xml"/>
<import resource="file:C:\guangju\workspace2\springlearn\src\main\resources\springmvc.xml"/>
相對路徑這麼寫(相對當期檔案):
比如說你的當前檔案再Context目錄下,你要引用的檔案再根目錄下,就這麼寫
<import resource="../springmvc.xml"/>
如果當前檔案和引用檔案在同一目錄下就這麼寫
<import resource="springmvc.xml"/>
<import resource="/springmvc.xml"/>
然後繼續看alias的解析方法processAliasRegistration
這個標籤是怎麼用的呢,主要是給註冊的bean註冊別名,預設情況下一個bean的id就可以索引到該bean但是如果不想用id,或者在不同的場景下用不同的名稱就需要用標籤<alias>
具體這麼用
<bean id="dao" class="com.pactera.aop.DaoImpl" ></bean>
<alias name="dao" alias="dao1"/>
這樣這個bean就有個兩個別名,用dao,dao1都可以得到此bean.看程式碼。protected void processAliasRegistration(Element ele) {
//獲取name屬性
String name = ele.getAttribute(NAME_ATTRIBUTE);
//獲取別名
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
//註冊
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
註冊程式碼registerAliaspublic void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
//當前的別名和name相同,在別名中移除此別名
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
//別名在aliasMap已經存在直接返回
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
//如果是迴圈引用報錯
checkForAliasCircle(name, alias);
//別名和beanname放入map
this.aliasMap.put(alias, name);
}
}
典型的迴圈引用
<bean id="dao" class="com.pactera.aop.DaoImpl" name="dao1"></bean>
<alias name="dao1" alias="dao"/>