前言
緊跟上篇Spring解密- XML解析與Bean註冊,我們接著往下分析源碼,話不多說了,來一起看看詳細的介紹吧。
解密
在Spring 的XML 配置裡面有兩大類聲明,一個是默認的如<bean id="person"/> ,另一類就是自定義的如<tx:annotation-driven /> ,兩種標籤的解析方式差異是非常大的。 parseBeanDefinitions 方法就是用來區分不同標籤所使用的解析方式。通過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不為空的情況下若存在默認標籤的子節點下再有自定義屬性,還需要再次對自定義標籤進行解析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); } // 最後發出響應事件,通知相關監聽器這個bean已經被加載getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }}這段代碼中:
下面我們詳細分析下,Spring 是如何解析各個標籤和節點的
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屬性的值通過,; 進行分割轉為字符串數字(即在配置文件中如配置多個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的唯一性// 內部核心為使用usedNames集合保存所有已經使用了的beanName和alisa checkNameUniqueness(beanName, aliases, ele); } // 進一步解析其他所有屬性到GenericBeanDefinition對像中AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { // 如果bean沒有指定beanName 那麼使用默認規則為此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 等相關屬性,生成了beanName,並且在重載函數parseBeanDefinitionElement(ele, beanName, containingBean)方法中完成核心的標籤解析。
接下來重點分析parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
看下它是如何完成標籤解析操作的
bean 節點與屬性解析
@Nullablepublic 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 { // 創建用於承載屬性的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;}進一步解析其他屬性和元素(元素和屬性很多,所以這是一個龐大的工作量)並統一封裝至GenericBeanDefinition 中, 解析完成這些屬性和元素之後,如果檢測到bean 沒有指定的beanName,那麼便使用默認的規則為bean 生成一個beanName。
// BeanDefinitionParserDelegate.javaprotected 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> 在容器中的內部表示形式,BeanDefinition 和<bean> 是一一對應的。同時BeanDefinition 會被註冊到BeanDefinitionRegistry 中,BeanDefinitionRegistry 就像Spring 配置信息的內存數據庫。
至此createBeanDefinition(className, parent);已經說完了,而且我們也獲得了用於承載屬性的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` 標籤的完整解析到這就已經全部結束了,其中`bean` 標籤下的元素解析都大同小異,有興趣的可以自己跟踪一下源代碼看看`qualifier、lookup-method` 等解析方式(*相對`bean` 而言不復雜*)。自定義標籤內容較多會在下一章詳細介紹。
最後將獲取到的信息封裝到`BeanDefinitionHolder` 實例中
``` java// BeanDefinitionParserDelegate.java@Nullablepublic BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // ... return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}註冊解析的BeanDefinition
在解析完配置文件後我們已經獲取了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()) { // eg 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 { // 判斷是否已經開始創建bean if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { // 保存beanDefinition到beanDefinitionMap中this.beanDefinitionMap.put(beanName, beanDefinition); // 更新已經註冊的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 { // 還沒開始創建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); } }}註冊別名
註冊好了beanDefinition,接下來就是註冊alias。註冊的alias 和beanName 的對應關係存放在了aliasMap 中,沿著類的繼承鏈會發現registerAlias 的方法是在SimpleAliasRegistry 中實現的
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)) { // 如果別名已經註冊過並且指向的name和當前name相同不做任何處理return; } // 如果alias不允許被覆蓋則拋出異常if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } } // 校驗循環指向依賴如A->B B->C C->A則出錯checkForAliasCircle(name, alias); this.aliasMap.put(alias, name); } }}通過checkForAliasCircle()方法來檢查alias 循環依賴,當A -> B 存在時,若再次出現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;}至此,註冊別名也完成了,主要完成了以下幾個工作
發送通知
通知監聽器解析及註冊完成
//DefaultBeanDefinitionDocumentReader.javaprotected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}通過fireComponentRegistered 方法進行通知監聽器解析及註冊完成工作,這裡的實現只為擴展,當程序開發人員需要對註冊BeanDefinition事件進行監聽時,可以通過註冊監聽器的方式並將處理邏輯寫入監聽器中,目前Spring 中並沒有對此事件做任何處理
其中ReaderContext 是在類XmlBeanDefinitionReader 中調用createReaderContext 生成的,然後調用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); } // 別名註冊後告知監聽器做相應處理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*:" } // 如果是絕對路徑則直接根據地址加載對應的配置文件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; // 根據相對路徑加載資源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)); }}完成了對import 標籤的處理,首先就是獲取<import resource="beans.xml"/> resource 屬性所表示的路徑,接著解析路徑中的屬性佔位符如${user.dir} ,然後判定location 是絕對路徑還是相對路徑,如果是絕對路徑則遞歸調用bean 的解析過程(loadBeanDefinitions(location, actualResources);) ,進行另一次解析,如果是相對路徑則計算出絕對路徑並進行解析,最後通知監聽器,解析完成
總結
熬過幾個無人知曉的秋冬春夏,撐過去一切都會順著你想要的方向走…
好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。
說點什麼
全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter1(本地下載)