Spring源碼解密之默認(rèn)標(biāo)簽的解析
前言
緊跟上篇 Spring解密 - XML解析 與 Bean注冊(cè) ,我們接著往下分析源碼,話不多說了,來一起看看詳細(xì)的介紹吧。
解密
在 Spring 的 XML 配置里面有兩大類聲明,一個(gè)是默認(rèn)的如 <bean id="person" class="com.battcn.bean.Person"/> ,另一類就是自定義的如<tx:annotation-driven /> ,兩種標(biāo)簽的解析方式差異是非常大的。parseBeanDefinitions 方法就是用來區(qū)分不同標(biāo)簽所使用的解析方式。通過 node.getNamespaceURI() 方法獲取命名空間,判斷是默認(rèn)命名空間還是自定義命名空間,并與 Spring 中固定的命名空間 http://www.springframework.org/schema/beans 進(jìn)行比對(duì),如果一致則采用parseDefaultElement(ele, delegate);否則就是delegate.parseCustomElement(ele);
默認(rèn)標(biāo)簽的解析
parseDefaultElement 對(duì) 4 種不同的標(biāo)簽 import、alias、bean、beans 做了不同的處理,其中 bean 標(biāo)簽的解析最為復(fù)雜也最為重要,所以我們將從 bean 開始深入分析,如果能理解此標(biāo)簽的解析過程,其他標(biāo)簽的解析自然會(huì)迎刃而解。上一篇中只是簡單描述了一下,本篇我們圍繞解析模塊詳細(xì)的探討一下
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
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);
}
// import 標(biāo)簽解析
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// beans標(biāo)簽解析 遞歸方式
doRegisterBeanDefinitions(ele);
}
}
}

首先我們來分析下當(dāng)類中的 processBeanDefinition(ele, delegate)
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 委托BeanDefinitionDelegate類的parseBeanDefinitionElement方法進(jìn)行元素解析
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 當(dāng)返回的bdHolder不為空的情況下若存在默認(rèn)標(biāo)簽的子節(jié)點(diǎn)下再有自定義屬性,還需要再次對(duì)自定義標(biāo)簽進(jìn)行解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 解析完成后需要對(duì)解析后的bdHolder進(jìn)行注冊(cè),注冊(cè)操作委托給了BeanDefinitionReaderUtils的registerBeanDefinition方法
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 最后發(fā)出響應(yīng)事件,通知相關(guān)監(jiān)聽器這個(gè)bean已經(jīng)被加載
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
這段代碼中:
- 首先委托 BeanDefinitionParseDelegate 對(duì)節(jié)點(diǎn)做了解析,并返回了一個(gè) BeanDefinitionHolder 的實(shí)例,在這個(gè)實(shí)例中已經(jīng)包含了配置文件中配置的各種屬性了
- 如果在當(dāng)前子節(jié)點(diǎn)中存在自定義屬性,則還需要對(duì)自定義標(biāo)簽進(jìn)行解析
- 解析完成后,需要對(duì)解析后的 bdHolder 進(jìn)行注冊(cè),同樣注冊(cè)操作委托給了 BeanDefinitionReaderUtils
- 最后發(fā)出響應(yīng)事件,通知相關(guān)監(jiān)聽器這個(gè) bean 已經(jīng)被加載
下面我們?cè)敿?xì)分析下,Spring 是如何解析各個(gè)標(biāo)簽和節(jié)點(diǎn)的
bean 標(biāo)簽解析
public class BeanDefinitionParserDelegate {
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 獲取Bean標(biāo)簽的ID屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 獲取Bean標(biāo)簽的Name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
// 將name屬性的值通過,; 進(jìn)行分割 轉(zhuǎn)為字符串?dāng)?shù)字(即在配置文件中如配置多個(gè)name 在此做處理)
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
// 如果ID為空 使用配置的第一個(gè)name屬性作為ID
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 校驗(yàn)beanName和aliases的唯一性
// 內(nèi)部核心為使用usedNames集合保存所有已經(jīng)使用了的beanName和alisa
checkNameUniqueness(beanName, aliases, ele);
}
// 進(jìn)一步解析其他所有屬性到GenericBeanDefinition對(duì)象中
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 如果bean沒有指定beanName 那么使用默認(rèn)規(guī)則為此Bean生成beanName
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
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.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);
// 將信息封裝到BeanDefinitionHolder對(duì)象中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
}
該方法主要處理了 id、name、alias 等相關(guān)屬性,生成了 beanName,并且在重載函數(shù) parseBeanDefinitionElement(ele, beanName, containingBean)方法中完成核心的標(biāo)簽解析。
接下來重點(diǎn)分析parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
看下它是如何完成標(biāo)簽解析操作的
bean 節(jié)點(diǎn)與屬性解析
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 獲取Bean標(biāo)簽的class屬性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 獲取Bean標(biāo)簽的parent屬性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 創(chuàng)建用于承載屬性的AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 獲取bean標(biāo)簽各種屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 解析description標(biāo)簽
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析meta標(biāo)簽
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)簽
parsePropertyElements(ele, bd);
// 解析qualifier標(biāo)簽
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;
}
進(jìn)一步解析其他屬性和元素(元素和屬性很多,所以這是一個(gè)龐大的工作量)并統(tǒng)一封裝至 GenericBeanDefinition 中, 解析完成這些屬性和元素之后,如果檢測到 bean 沒有指定的 beanName,那么便使用默認(rèn)的規(guī)則為 bean 生成一個(gè) beanName。

// BeanDefinitionParserDelegate.java
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
public class BeanDefinitionReaderUtils {
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
// parentName可能為空
bd.setParentName(parentName);
// 如果classLoader不為空
// 則使用傳入的classLoader同一虛擬機(jī)加載類對(duì)象 否則只記錄classLoader
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
}
BeanDefinition 是 <bean> 在容器中的內(nèi)部表示形式,BeanDefinition 和 <bean> 是一一對(duì)應(yīng)的。同時(shí) BeanDefinition 會(huì)被注冊(cè)到 BeanDefinitionRegistry 中,BeanDefinitionRegistry 就像 Spring 配置信息的內(nèi)存數(shù)據(jù)庫。
至此 createBeanDefinition(className, parent); 已經(jīng)說完了,而且我們也獲得了 用于承載屬性的AbstractBeanDefinition,接下來看看 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 是如何解析 bean 中的各種標(biāo)簽屬性的
public class BeanDefinitionParserDelegate {
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// ...省略詳細(xì)代碼,該部分代碼主要就是通過 if else 判斷是否含有指定的屬性,如果有就 bd.set(attribute);
return bd;
}
}
```
`bean` 標(biāo)簽的完整解析到這就已經(jīng)全部結(jié)束了,其中 `bean` 標(biāo)簽下的元素解析都大同小異,有興趣的可以自己跟蹤一下源代碼看看 `qualifier、lookup-method` 等解析方式(*相對(duì) `bean` 而言不復(fù)雜*)。自定義標(biāo)簽內(nèi)容較多會(huì)在下一章詳細(xì)介紹。
最后將獲取到的信息封裝到 `BeanDefinitionHolder` 實(shí)例中
``` java
// BeanDefinitionParserDelegate.java
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// ...
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
注冊(cè)解析的 BeanDefinition
在解析完配置文件后我們已經(jīng)獲取了 bean 的所有屬性,接下來就是對(duì) bean 的注冊(cè)了
public class BeanDefinitionReaderUtils {
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用 beanName 做唯一標(biāo)識(shí)符
String beanName = definitionHolder.getBeanName();
// 注冊(cè)bean的核心代碼
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 為bean注冊(cè)所有的別名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
}
以上代碼主要完成兩個(gè)功能,一是使用 beanName 注冊(cè) beanDefinition,二是完成了對(duì)別名的注冊(cè)

BeanName 注冊(cè) BeanDefinition
public class DefaultListableBeanFactory {
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 注冊(cè)前的最后一次校驗(yàn),這里的校驗(yàn)不同于XML文件校驗(yàn)
// 主要是對(duì)于AbstractBeanDefinition屬性中的methodOverrides校驗(yàn)
// 校驗(yàn)methodOverrides是否與工廠方法并存或者methodOverrides對(duì)于的方法根本不存在
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
// 獲取緩存中的 beanDefinition
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
// 如果緩存中存在 判斷是否允許覆蓋
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 如果允許覆蓋,保存beanDefinition到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 判斷是否已經(jīng)開始創(chuàng)建bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 保存beanDefinition到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 更新已經(jīng)注冊(cè)的beanName
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// 還沒開始創(chuàng)建bean
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
// 重置beanName對(duì)應(yīng)的緩存
resetBeanDefinition(beanName);
}
}
}
- 對(duì) AbstractBeanDefinition 的校驗(yàn),主要是針對(duì) AbstractBeanDefinition 的 methodOverrides 屬性的
- 對(duì) beanName 已經(jīng)注冊(cè)的情況的處理,如果設(shè)置了不允許 bean 的覆蓋,則需要拋出異常,否則直接覆蓋
- 使用 beanName 作為 key,beanDefinition 為 Value 加入 beanDefinitionMap 存儲(chǔ)
- 如果緩存中已經(jīng)存在,并且該 bean 為單例模式則清楚 beanName 對(duì)應(yīng)的緩存
注冊(cè)別名
注冊(cè)好了 beanDefinition,接下來就是注冊(cè) alias。注冊(cè)的 alias 和 beanName 的對(duì)應(yīng)關(guān)系存放在了 aliasMap 中,沿著類的繼承鏈會(huì)發(fā)現(xiàn) registerAlias 的方法是在 SimpleAliasRegistry 中實(shí)現(xiàn)的
public class SimpleAliasRegistry {
/** Map from alias to canonical name */
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
if (alias.equals(name)) {
// 如果beanName與alias相同的話不記錄alias 并刪除對(duì)應(yīng)的alias
this.aliasMap.remove(alias);
} else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// 如果別名已經(jīng)注冊(cè)過并且指向的name和當(dāng)前name相同 不做任何處理
return;
}
// 如果alias不允許被覆蓋則拋出異常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 校驗(yàn)循環(huán)指向依賴 如A->B B->C C->A則出錯(cuò)
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
}
通過 checkForAliasCircle() 方法來檢查 alias 循環(huán)依賴,當(dāng) A -> B 存在時(shí),若再次出現(xiàn) A -> C -> B 則會(huì)拋出異常:
protected void checkForAliasCircle(String name, String alias) {
if (hasAlias(alias, name)) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
public boolean hasAlias(String name, String alias) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String registeredAlias = entry.getKey();
return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
}
}
return false;
}
至此,注冊(cè)別名也完成了,主要完成了以下幾個(gè)工作
- 如果 beanName 與 alias 相同的話不記錄 alias 并刪除對(duì)應(yīng)的 alias
- 如果別名已經(jīng)注冊(cè)過并且指向的name和當(dāng)前name相同 不做任何處理
- 如果別名已經(jīng)注冊(cè)過并且指向的name和當(dāng)前name不相同 判斷是否允許被覆蓋
- 校驗(yàn)循環(huán)指向依賴 如A->B B->C C->A則出錯(cuò)
發(fā)送通知
通知監(jiān)聽器解析及注冊(cè)完成
//DefaultBeanDefinitionDocumentReader.java
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
通過 fireComponentRegistered 方法進(jìn)行通知監(jiān)聽器解析及注冊(cè)完成工作,這里的實(shí)現(xiàn)只為擴(kuò)展,當(dāng)程序開發(fā)人員需要對(duì)注冊(cè) BeanDefinition事件進(jìn)行監(jiān)聽時(shí),可以通過注冊(cè)監(jiān)聽器的方式并將處理邏輯寫入監(jiān)聽器中,目前 Spring 中并沒有對(duì)此事件做任何處理
其中 ReaderContext 是在類 XmlBeanDefinitionReader 中調(diào)用 createReaderContext 生成的,然后調(diào)用 fireComponentRegistered()
alias 標(biāo)簽解析
Spring 提供了 <alias name="person" alias="p"/> 方式來進(jìn)行別名的配置,該標(biāo)簽解析是在 processAliasRegistration(Element ele) 方法中完成的
public class DefaultBeanDefinitionDocumentReader {
protected void processAliasRegistration(Element ele) {
// 獲取 alisa 標(biāo)簽 name 屬性
String name = ele.getAttribute(NAME_ATTRIBUTE);
// 獲取 alisa 標(biāo)簽 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 {
// 進(jìn)行別名注冊(cè)
getReaderContext().getRegistry().registerAlias(name, alias);
} catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
// 別名注冊(cè)后告知監(jiān)聽器做相應(yīng)處理
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
}
首先對(duì) alias 標(biāo)簽屬性進(jìn)行提取校驗(yàn),校驗(yàn)通過后進(jìn)行別名注冊(cè),別名注冊(cè)和 bean 標(biāo)簽解析中的別名注冊(cè)一直,此處不再贅述
import 標(biāo)簽解析
public class DefaultBeanDefinitionDocumentReader {
protected void importBeanDefinitionResource(Element ele) {
// 獲取import標(biāo)簽的resource屬性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果不存在則不做任何處理
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// 解析占位符屬性 格式如"${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 判斷資源是絕對(duì)路徑還是相對(duì)路徑
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*:"
}
// 如果是絕對(duì)路徑則直接根據(jù)地址加載對(duì)應(yīng)的配置文件
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 {
try {
int importCount;
// 根據(jù)相對(duì)路徑加載資源
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);
}
}
// 解析后進(jìn)行監(jiān)聽器激活處理
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
}
完成了對(duì) import 標(biāo)簽的處理,首先就是獲取 <import resource="beans.xml"/> resource 屬性所表示的路徑,接著解析路徑中的屬性占位符 如 ${user.dir} ,然后判定 location 是絕對(duì)路徑還是相對(duì)路徑,如果是絕對(duì)路徑則遞歸調(diào)用 bean 的解析過程(loadBeanDefinitions(location, actualResources);) ,進(jìn)行另一次解析,如果是相對(duì)路徑則計(jì)算出絕對(duì)路徑并進(jìn)行解析,最后通知監(jiān)聽器,解析完成
總結(jié)
熬過幾個(gè)無人知曉的秋冬春夏,撐過去一切都會(huì)順著你想要的方向走…
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
說點(diǎn)什么
全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter1(本地下載)
相關(guān)文章
詳解如何快速定位和解決JSON錯(cuò)誤(以Protobuf的JsonFormat.ParseException為例)
在開發(fā)過程中,JSON數(shù)據(jù)的解析是一個(gè)常見的操作,尤其是在微服務(wù)架構(gòu)中,服務(wù)之間的通信通常依賴于JSON格式的數(shù)據(jù),然而,JSON數(shù)據(jù)的格式錯(cuò)誤往往會(huì)導(dǎo)致解析失敗,進(jìn)而引發(fā)系統(tǒng)異常,本文將以一個(gè)實(shí)際的錯(cuò)誤案例為例,詳細(xì)講解如何快速定位和解決JSON解析錯(cuò)誤2025-03-03
SpringBoot數(shù)據(jù)校驗(yàn)及多環(huán)境配置的問題詳解
這篇文章主要介紹了SpringBoot數(shù)據(jù)校驗(yàn)及多環(huán)境配置,本文以SpringBoot-02-Config 項(xiàng)目為例,給大家詳細(xì)介紹,需要的朋友可以參考下2021-09-09
Spring MVC 接口 ResponseBodyAdvice 及其應(yīng)用最佳實(shí)
ResponseBodyAdvice?是 Spring MVC 提供的一個(gè)強(qiáng)大接口,允許你在響應(yīng)體被寫入 HTTP 響應(yīng)之前對(duì)其進(jìn)行全局處理,下面我將全面介紹它的工作原理、使用場景和最佳實(shí)踐,感興趣的朋友一起看看吧2025-04-04
File.createTempFile創(chuàng)建臨時(shí)文件的示例詳解
這篇文章主要介紹了File.createTempFile創(chuàng)建臨時(shí)文件的示例詳解,在默認(rèn)臨時(shí)文件目錄中創(chuàng)建一個(gè)空文件,使用給定前綴和后綴生成其名稱。 如果感興趣來了解一下2020-07-07
java使用Base64實(shí)現(xiàn)文件加密解密
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)Base64給文件加密、解密,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03
Spring JdbcTemplate執(zhí)行數(shù)據(jù)庫操作詳解
JdbcTemplate是Spring框架自帶的對(duì)JDBC操作的封裝,目的是提供統(tǒng)一的模板方法使對(duì)數(shù)據(jù)庫的操作更加方便、友好,效率也不錯(cuò),這篇文章主要介紹了Spring JdbcTemplate執(zhí)行數(shù)據(jù)庫操作,需要的朋友可以參考下2022-10-10

