欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring源碼解密之默認標簽的解析

 更新時間:2018年01月15日 10:31:26   作者:唐亞峰  
這篇文章主要給大家介紹了關(guān)于Spring源碼解密之默認標簽的解析的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。

前言

緊跟上篇 Spring解密 - XML解析 與 Bean注冊 ,我們接著往下分析源碼,話不多說了,來一起看看詳細的介紹吧。

解密

在 Spring 的 XML 配置里面有兩大類聲明,一個是默認的如 <bean id="person" class="com.battcn.bean.Person"/> ,另一類就是自定義的如<tx:annotation-driven /> ,兩種標簽的解析方式差異是非常大的。parseBeanDefinitions 方法就是用來區(qū)分不同標簽所使用的解析方式。通過 node.getNamespaceURI() 方法獲取命名空間,判斷是默認命名空間還是自定義命名空間,并與 Spring 中固定的命名空間 http://www.springframework.org/schema/beans 進行比對,如果一致則采用parseDefaultElement(ele, delegate);否則就是delegate.parseCustomElement(ele);

默認標簽的解析

parseDefaultElement 對 4 種不同的標簽 import、alias、bean、beans 做了不同的處理,其中 bean 標簽的解析最為復雜也最為重要,所以我們將從 bean 開始深入分析,如果能理解此標簽的解析過程,其他標簽的解析自然會迎刃而解。上一篇中只是簡單描述了一下,本篇我們圍繞解析模塊詳細的探討一下

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
 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);
 }
 // import 標簽解析
 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
 // beans標簽解析 遞歸方式 
 doRegisterBeanDefinitions(ele);
 }
 }
}

首先我們來分析下當類中的 processBeanDefinition(ele, delegate)

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
 // 委托BeanDefinitionDelegate類的parseBeanDefinitionElement方法進行元素解析
 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
 if (bdHolder != null) {
 // 當返回的bdHolder不為空的情況下若存在默認標簽的子節(jié)點下再有自定義屬性,還需要再次對自定義標簽進行解析
 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
 try {
 // 解析完成后需要對解析后的bdHolder進行注冊,注冊操作委托給了BeanDefinitionReaderUtils的registerBeanDefinition方法
 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
 }
 catch (BeanDefinitionStoreException ex) {
 getReaderContext().error("Failed to register bean definition with name '" +
 bdHolder.getBeanName() + "'", ele, ex);
 }
 // 最后發(fā)出響應事件,通知相關(guān)監(jiān)聽器這個bean已經(jīng)被加載
 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
 }
}

這段代碼中:

  • 首先委托 BeanDefinitionParseDelegate 對節(jié)點做了解析,并返回了一個 BeanDefinitionHolder 的實例,在這個實例中已經(jīng)包含了配置文件中配置的各種屬性了
  • 如果在當前子節(jié)點中存在自定義屬性,則還需要對自定義標簽進行解析
  • 解析完成后,需要對解析后的 bdHolder 進行注冊,同樣注冊操作委托給了 BeanDefinitionReaderUtils
  • 最后發(fā)出響應事件,通知相關(guān)監(jiān)聽器這個 bean 已經(jīng)被加載

下面我們詳細分析下,Spring 是如何解析各個標簽和節(jié)點的

bean 標簽解析

public class BeanDefinitionParserDelegate {
 @Nullable
 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
 // 獲取Bean標簽的ID屬性
 String id = ele.getAttribute(ID_ATTRIBUTE);
 // 獲取Bean標簽的Name屬性
 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
 List<String> aliases = new ArrayList<>();
 if (StringUtils.hasLength(nameAttr)) {
 // 將name屬性的值通過,; 進行分割 轉(zhuǎn)為字符串數(shù)字(即在配置文件中如配置多個name 在此做處理)
 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
 aliases.addAll(Arrays.asList(nameArr));
 }
 String beanName = id;
 // 如果ID為空 使用配置的第一個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) {
 // 校驗beanName和aliases的唯一性
 // 內(nèi)部核心為使用usedNames集合保存所有已經(jīng)使用了的beanName和alisa
 checkNameUniqueness(beanName, aliases, ele);
 }
 // 進一步解析其他所有屬性到GenericBeanDefinition對象中
 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
 if (beanDefinition != null) {
 // 如果bean沒有指定beanName 那么使用默認規(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對象中
 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
 }
 return null;
 }
}

該方法主要處理了 id、name、alias 等相關(guān)屬性,生成了 beanName,并且在重載函數(shù) parseBeanDefinitionElement(ele, beanName, containingBean)方法中完成核心的標簽解析。

接下來重點分析parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)

看下它是如何完成標簽解析操作的

bean 節(jié)點與屬性解析

@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
 Element ele, String beanName, @Nullable BeanDefinition containingBean) {
 this.parseState.push(new BeanEntry(beanName));
 // 獲取Bean標簽的class屬性
 String className = null;
 if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
 className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
 }
 // 獲取Bean標簽的parent屬性
 String parent = null;
 if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
 parent = ele.getAttribute(PARENT_ATTRIBUTE);
 }
 try {
 // 創(chuàng)建用于承載屬性的AbstractBeanDefinition
 AbstractBeanDefinition bd = createBeanDefinition(className, parent);
 // 獲取bean標簽各種屬性
 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
 // 解析description標簽
 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
 // 解析meta標簽
 parseMetaElements(ele, bd);
 // 解析lookup-method標簽
 parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
 // 解析replaced-method標簽
 parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
 // 解析constructor-arg標簽
 parseConstructorArgElements(ele, bd);
 // 解析property標簽
 parsePropertyElements(ele, bd);
 // 解析qualifier標簽
 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;
}

進一步解析其他屬性和元素(元素和屬性很多,所以這是一個龐大的工作量)并統(tǒng)一封裝至 GenericBeanDefinition 中, 解析完成這些屬性和元素之后,如果檢測到 bean 沒有指定的 beanName,那么便使用默認的規(guī)則為 bean 生成一個 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同一虛擬機加載類對象 否則只記錄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> 是一一對應的。同時 BeanDefinition 會被注冊到 BeanDefinitionRegistry 中,BeanDefinitionRegistry 就像 Spring 配置信息的內(nèi)存數(shù)據(jù)庫。

至此 createBeanDefinition(className, parent); 已經(jīng)說完了,而且我們也獲得了 用于承載屬性的AbstractBeanDefinition,接下來看看 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 是如何解析 bean 中的各種標簽屬性的

public class BeanDefinitionParserDelegate {
 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
 @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
 // ...省略詳細代碼,該部分代碼主要就是通過 if else 判斷是否含有指定的屬性,如果有就 bd.set(attribute);
 return bd;
 }
}
```

`bean` 標簽的完整解析到這就已經(jīng)全部結(jié)束了,其中 `bean` 標簽下的元素解析都大同小異,有興趣的可以自己跟蹤一下源代碼看看 `qualifier、lookup-method` 等解析方式(*相對 `bean` 而言不復雜*)。自定義標簽內(nèi)容較多會在下一章詳細介紹。

最后將獲取到的信息封裝到 `BeanDefinitionHolder` 實例中

``` java
// BeanDefinitionParserDelegate.java
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
 // ...
 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

注冊解析的 BeanDefinition

在解析完配置文件后我們已經(jīng)獲取了 bean 的所有屬性,接下來就是對 bean 的注冊了

public class BeanDefinitionReaderUtils {
 public static void registerBeanDefinition(
 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
 throws BeanDefinitionStoreException {
 // 使用 beanName 做唯一標識符
 String beanName = definitionHolder.getBeanName();
 // 注冊bean的核心代碼
 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
 // 為bean注冊所有的別名
 String[] aliases = definitionHolder.getAliases();
 if (aliases != null) {
 for (String alias : aliases) {
 registry.registerAlias(beanName, alias);
 }
 }
 }
}

以上代碼主要完成兩個功能,一是使用 beanName 注冊 beanDefinition,二是完成了對別名的注冊

BeanName 注冊 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 {
 // 注冊前的最后一次校驗,這里的校驗不同于XML文件校驗
 // 主要是對于AbstractBeanDefinition屬性中的methodOverrides校驗
 // 校驗methodOverrides是否與工廠方法并存或者methodOverrides對于的方法根本不存在
 ((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)注冊的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對應的緩存
 resetBeanDefinition(beanName);
 }
 }
}
  • 對 AbstractBeanDefinition 的校驗,主要是針對 AbstractBeanDefinition 的 methodOverrides 屬性的
  • 對 beanName 已經(jīng)注冊的情況的處理,如果設(shè)置了不允許 bean 的覆蓋,則需要拋出異常,否則直接覆蓋
  • 使用 beanName 作為 key,beanDefinition 為 Value 加入 beanDefinitionMap 存儲
  • 如果緩存中已經(jīng)存在,并且該 bean 為單例模式則清楚 beanName 對應的緩存

注冊別名

注冊好了 beanDefinition,接下來就是注冊 alias。注冊的 alias 和 beanName 的對應關(guān)系存放在了 aliasMap 中,沿著類的繼承鏈會發(fā)現(xiàn) registerAlias 的方法是在 SimpleAliasRegistry 中實現(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 并刪除對應的alias
  this.aliasMap.remove(alias);
 } else {
  String registeredName = this.aliasMap.get(alias);
  if (registeredName != null) {
  if (registeredName.equals(name)) {
   // 如果別名已經(jīng)注冊過并且指向的name和當前name相同 不做任何處理
   return;
  }
  // 如果alias不允許被覆蓋則拋出異常
  if (!allowAliasOverriding()) {
   throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
  }
  }
  // 校驗循環(huán)指向依賴 如A->B B->C C->A則出錯
  checkForAliasCircle(name, alias);
  this.aliasMap.put(alias, name);
 }
 }
}

通過 checkForAliasCircle() 方法來檢查 alias 循環(huán)依賴,當 A -> B 存在時,若再次出現(xiàn) A -> C -> B 則會拋出異常:

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;
}

至此,注冊別名也完成了,主要完成了以下幾個工作

  • 如果 beanName 與 alias 相同的話不記錄 alias 并刪除對應的 alias
  • 如果別名已經(jīng)注冊過并且指向的name和當前name相同 不做任何處理
  • 如果別名已經(jīng)注冊過并且指向的name和當前name不相同 判斷是否允許被覆蓋
  • 校驗循環(huán)指向依賴 如A->B B->C C->A則出錯

發(fā)送通知

通知監(jiān)聽器解析及注冊完成

//DefaultBeanDefinitionDocumentReader.java
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
 // Send registration event.
 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}

通過 fireComponentRegistered 方法進行通知監(jiān)聽器解析及注冊完成工作,這里的實現(xiàn)只為擴展,當程序開發(fā)人員需要對注冊 BeanDefinition事件進行監(jiān)聽時,可以通過注冊監(jiān)聽器的方式并將處理邏輯寫入監(jiān)聽器中,目前 Spring 中并沒有對此事件做任何處理

其中 ReaderContext 是在類 XmlBeanDefinitionReader 中調(diào)用 createReaderContext 生成的,然后調(diào)用 fireComponentRegistered()

alias 標簽解析

Spring 提供了 <alias name="person" alias="p"/> 方式來進行別名的配置,該標簽解析是在 processAliasRegistration(Element ele) 方法中完成的

public class DefaultBeanDefinitionDocumentReader {
 protected void processAliasRegistration(Element ele) {
 // 獲取 alisa 標簽 name 屬性
 String name = ele.getAttribute(NAME_ATTRIBUTE);
 // 獲取 alisa 標簽 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 {
  // 進行別名注冊
  getReaderContext().getRegistry().registerAlias(name, alias);
  } catch (Exception ex) {
  getReaderContext().error("Failed to register alias '" + alias +
   "' for bean with name '" + name + "'", ele, ex);
  }
  // 別名注冊后告知監(jiān)聽器做相應處理
  getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
 }
 }
}

首先對 alias 標簽屬性進行提取校驗,校驗通過后進行別名注冊,別名注冊和 bean 標簽解析中的別名注冊一直,此處不再贅述

import 標簽解析

public class DefaultBeanDefinitionDocumentReader {
 protected void importBeanDefinitionResource(Element ele) {
 // 獲取import標簽的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);
 // 判斷資源是絕對路徑還是相對路徑
 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*:"
 }
 
 // 如果是絕對路徑則直接根據(jù)地址加載對應的配置文件
 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ù)相對路徑加載資源
  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);
  }
 }
 // 解析后進行監(jiān)聽器激活處理
 Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
 getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
 }
}

完成了對 import 標簽的處理,首先就是獲取 <import resource="beans.xml"/> resource 屬性所表示的路徑,接著解析路徑中的屬性占位符 如 ${user.dir} ,然后判定 location 是絕對路徑還是相對路徑,如果是絕對路徑則遞歸調(diào)用 bean 的解析過程(loadBeanDefinitions(location, actualResources);) ,進行另一次解析,如果是相對路徑則計算出絕對路徑并進行解析,最后通知監(jiān)聽器,解析完成

總結(jié)

熬過幾個無人知曉的秋冬春夏,撐過去一切都會順著你想要的方向走…

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

說點什么

全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter1本地下載

您可能感興趣的文章:

相關(guān)文章

  • Java中的CountDownLatch、CyclicBarrier和semaphore實現(xiàn)原理解讀

    Java中的CountDownLatch、CyclicBarrier和semaphore實現(xiàn)原理解讀

    這篇文章主要介紹了Java中的CountDownLatch、CyclicBarrier和semaphore實現(xiàn)原理詳解,CountDownLatch中調(diào)用await方法線程需要等待所有調(diào)用countDown方法的線程執(zhí)行,這就很適合一個業(yè)務(wù)需要一些準備條件,等準備條件準備好之后再繼續(xù)執(zhí)行,需要的朋友可以參考下
    2023-12-12
  • 深入Java不可變類型的詳解

    深入Java不可變類型的詳解

    本篇文章是Java中的不可變類型進行了詳細的分析介紹,需要的朋友參考下
    2013-06-06
  • spring中使用Mockito解決Bean依賴樹問題方法

    spring中使用Mockito解決Bean依賴樹問題方法

    在本篇文章里小編給各位整理了關(guān)于spring中使用Mockito解決Bean依賴樹問題方法,有需要的朋友們可以參考下。
    2020-01-01
  • java 反射機制

    java 反射機制

    本文主要介紹了java反射機制的相關(guān)知識,具有一定的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • Java的布爾類型基本介紹

    Java的布爾類型基本介紹

    這篇文章主要介紹了Java的布爾類型,是Java入門學習中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-10-10
  • Java?Zookeeper分布式分片算法超詳細講解流程

    Java?Zookeeper分布式分片算法超詳細講解流程

    ZooKeeper是一個分布式的,開放源碼的分布式應用程序協(xié)調(diào)服務(wù),是Google的Chubby一個開源的實現(xiàn),是Hadoop和Hbase的重要組件。它是一個為分布式應用提供一致性的軟件,提供的功能包括:配置維護、域名服務(wù)、分布式同步、組服務(wù)等
    2023-03-03
  • 簡單操作實現(xiàn)Java jsp servlet文件上傳過程解析

    簡單操作實現(xiàn)Java jsp servlet文件上傳過程解析

    這篇文章主要介紹了簡單操作實現(xiàn)Java jsp servlet文件上傳過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 當mybatis返回值遇見內(nèi)部類的問題

    當mybatis返回值遇見內(nèi)部類的問題

    這篇文章主要介紹了當mybatis返回值遇見內(nèi)部類的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 六個Java集合使用時需要注意的事項

    六個Java集合使用時需要注意的事項

    這篇文章主要為大家詳細介紹了六個Java集合使用時需要注意的事項,文中的示例代碼講解詳細,對我們學習java有一定的幫助,需要的可以參考一下
    2023-01-01
  • maven配置阿里倉庫的方法步驟

    maven配置阿里倉庫的方法步驟

    這篇文章主要介紹了maven配置阿里倉庫的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12

最新評論