3.3 import
標簽的解析
??對于
Spring
配置文件的編寫,如果經歷過龐大項目的人,都有那種恐懼的心理,太多的配置文件.不過,分模塊是大多數人能想到的辦法,但是,怎么分模塊,那就仁者見仁 智者見智了.使用import
是個好辦法,例如我們可以構造這樣的Spring
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<import resource="customerContext.xml" />
<import resource="systemContext.xml" />
... ...
</beans>
??
applicationContext.xml
文件中使用import
的方式導入有模塊配置文件,以后若有新模塊的加入,那就可以簡單修改這個文件即可.這樣大大簡化了配置后期維護的復雜度,并使配置模塊化,易于管理.我們來看看Spring
是如何解析import
配置文件的.
protected void processAliasRegistration(Element ele) {
// 獲取beanName
String name = ele.getAttribute(NAME_ATTRIBUTE);
// 獲取alias
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 {
// 注冊alias
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));
}
}
protected void importBeanDefinitionResource(Element ele) {
// 獲取resource屬性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果不存在resource屬性則不做任何處理
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// Resolve system properties: e.g. "${user.dir}"
// 解析系統屬性,格式如: "${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// Discover whether the location is an absolute or relative URI
// 判斷location是絕對URI還是相對URI
boolean absoluteLocation = false;
try {
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?
// 如果是絕對URI則直接根據地址加載對應的配置文件
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存在多個子實現類,如VfsResource, FileSystemResource等,
// 而每個resource的createRelative方式實現都不一樣,所以這里先使用子類的方法嘗試解析
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
// 如果解析不成功,則使用默認的解析器ResourcePatternResolver進行解析.
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[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
??上面的代碼不難,相信配合注解會很好理解的,我們總結一下大致流程便于大家更好的梳理,在解析
<import>
標簽時,Spring
進行解析的步驟大致如下.
?(1) 獲取resource
屬性鎖表示的路徑;
?(2) 解析路徑中的系統屬性,格式如"${user.dir}";
?(3) 判定location
是絕對路徑還是相對路徑;
?(4) 如果是絕對路徑則遞歸調用bean
的解析過程,進行另一次的解析;
?(5) 如果是相對路徑則計算出絕對路徑并進行解析;
?(6) 通知監聽器,解析完成.
3.4 嵌入式beans
標簽的解析
??對于嵌入式的
beans
標簽,相信大家使用過或者至少接觸過,非常類似于import
標簽所提供的功能,使用如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="aa" class="test.aa" />
<beans></beans>
</beans>
??對于嵌入式
beans
標簽來講,并沒有太多可講,與單獨的配置文件并沒有太大的差別,無非是遞歸調用beans
的解析過程,相信大家根據之前講解過的內容已經有能力理解其中的奧秘了.