概述
接前兩篇文章 spring原始碼深度解析—Spring的整體架構和環境搭建 和 spring原始碼深度解析— IOC 之 容器的基本實現
本文主要研究Spring標籤的解析,Spring的標籤中有預設標籤和自定義標籤,兩者的解析有著很大的不同,這次重點說預設標籤的解析過程。
預設標籤的解析是在DefaultBeanDefinitionDocumentReader.parseDefaultElement函式中進行的,分別對4種不同的標籤(import,alias,bean和beans)做了不同處理。我們先看下此函式的原始碼:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
Bean標籤的解析及註冊
在4中標籤中對bean標籤的解析最為複雜也最為重要,所以從此標籤開始深入分析,如果能理解這個標籤的解析過程,其他標籤的解析就迎刃而解了。對於bean標籤的解析用的是processBeanDefinition函式,首先看看函式processBeanDefinition(ele,delegate),其程式碼如下:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
剛開始看這個函式體時一頭霧水,沒有以前的函式那樣的清晰的邏輯,我們細緻的理下邏輯,大致流程如下:
- 首先委託BeanDefinitionDelegate類的parseBeanDefinitionElement方法進行元素的解析,返回BeanDefinitionHolder型別的例項bdHolder,經過這個方法後bdHolder例項已經包含了我們配置檔案中的各種屬性了,例如class,name,id,alias等。
- 當返回的dbHolder不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要再次對自定義標籤進行解析。
- 當解析完成後,需要對解析後的bdHolder進行註冊,註冊過程委託給了BeanDefinitionReaderUtils的registerBeanDefinition方法。
- 最後發出響應事件,通知相關的監聽器已經載入完這個Bean了。
解析BeanDefinition
接下來我們就針對具體的方法進行分析,首先我們從元素解析及資訊提取開始,也就是BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),進入 BeanDefinitionDelegate 類的 parseBeanDefinitionElement 方法。我們看下原始碼:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // 解析 ID 屬性 String id = ele.getAttribute(ID_ATTRIBUTE); // 解析 name 屬性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 分割 name 屬性 List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = 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"); } } // 檢查 name 的唯一性 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 解析 屬性,構造 AbstractBeanDefinition AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { // 如果 beanName 不存在,則根據條件構造一個 beanName if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); 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; }
上述方法就是對預設標籤解析的全過程了,我們分析下當前層完成的工作:
(1)提取元素中的id和name屬性
(2)進一步解析其他所有屬性並統一封裝到GenericBeanDefinition型別的例項中
(3)如果檢測到bean沒有指定beanName,那麼使用預設規則為此bean生成beanName。
(4)將獲取到的資訊封裝到BeanDefinitionHolder的例項中。
程式碼:AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);是用來對標籤中的其他屬性進行解析,我們詳細看下原始碼:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; //解析class屬性 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; //解析parent屬性 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { //建立用於承載屬性的AbstractBeanDefinition型別的GenericBeanDefinition例項 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //硬編碼解析bean的各種屬性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //設定description屬性 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //解析元素 parseMetaElements(ele, bd); //解析lookup-method屬性 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //解析replace-method屬性 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析建構函式的引數 parseConstructorArgElements(ele, bd); //解析properties子元素 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; }
接下來我們一步步分析解析過程。
bean詳細解析過程
建立用於承載屬性的BeanDefinition
BeanDefinition是一個介面,在spring中此介面有三種實現:RootBeanDefinition、ChildBeanDefinition已經GenericBeanDefinition。而三種實現都繼承了AbstractBeanDefinition,其中BeanDefinition是配置檔案元素標籤在容器中的內部表示形式。元素標籤擁有class、scope、lazy-init等屬性,BeanDefinition則提供了相應的beanClass、scope、lazyInit屬性,BeanDefinition和<bean>中的屬性一一對應。其中RootBeanDefinition是最常用的實現類,他對應一般性的元素標籤,GenericBeanDefinition是自2.5版本以後新加入的bean檔案配置屬性定義類,是一站式服務的。
在配置檔案中可以定義父和字,父用RootBeanDefinition表示,而子用ChildBeanDefinition表示,而沒有父的就使用RootBeanDefinition表示。AbstractBeanDefinition對兩者共同的類資訊進行抽象。
Spring通過BeanDefinition將配置檔案中的配置資訊轉換為容器的內部表示,並將這些BeanDefinition註冊到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置資訊的記憶體資料庫,主要是以map的形式儲存,後續操作直接從BeanDefinitionResistry中讀取配置資訊。它們之間的關係如下圖所示:
因此,要解析屬性首先要建立用於承載屬性的例項,也就是建立GenericBeanDefinition型別的例項。而程式碼createBeanDefinition(className,parent)的作用就是實現此功能。我們詳細看下方法體,程式碼如下:
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); } public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException { GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setParentName(parentName); if (className != null) { if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd; }
各種屬性的解析
當建立好了承載bean資訊的例項後,接下來就是解析各種屬性了,首先我們看下parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);方法,程式碼如下:
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { //解析singleton屬性 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } //解析scope屬性 else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } else if (containingBean != null) { // Take default from containing bean in case of an inner bean definition. bd.setScope(containingBean.getScope()); } //解析abstract屬性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } //解析lazy_init屬性 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); //解析autowire屬性 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); //解析dependsOn屬性 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } //解析autowireCandidate屬性 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } //解析primary屬性 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } //解析init_method屬性 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } else if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } //解析destroy_method屬性 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } else if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } //解析factory_method屬性 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } //解析factory_bean屬性 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
解析meta元素
在開始對meta元素解析分析前我們先簡單回顧下meta屬性的使用,簡單的示例程式碼如下:
<bean id="demo" class="com.yhl.myspring.demo.bean.MyBeanDemo">
<property name="beanName" value="bean demo1"/>
<meta key="demo" value="demo"/>
</bean>
這段程式碼並不會提現在demo的屬性中,而是一個額外的宣告,如果需要用到這裡面的資訊時可以通過BeanDefinition的getAttribute(key)方法獲取,對meta屬性的解析用的是:parseMetaElements(ele, bd);具體的方法體如下:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) { NodeList nl = ele.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) { Element metaElement = (Element) node; String key = metaElement.getAttribute(KEY_ATTRIBUTE); String value = metaElement.getAttribute(VALUE_ATTRIBUTE); BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value); attribute.setSource(extractSource(metaElement)); attributeAccessor.addMetadataAttribute(attribute); } } }
解析replaced-method屬性
在分析程式碼前我們還是先簡單的瞭解下replaced-method的用法,其主要功能是方法替換:即在執行時用新的方法替換舊的方法。與之前的lookup-method不同的是此方法不僅可以替換返回的bean,還可以動態的更改原有方法的執行邏輯,我們看下使用:
//原有的changeMe方法 public class TestChangeMethod { public void changeMe() { System.out.println("ChangeMe"); } } //新的實現方法 public class ReplacerChangeMethod implements MethodReplacer { public Object reimplement(Object o, Method method, Object[] objects) throws Throwable { System.out.println("I Replace Method"); return null; } } //新的配置檔案 <?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="changeMe" class="com.chenhao.spring.TestChangeMethod"> <replaced-method name="changeMe" replacer="replacer"/> </bean> <bean id="replacer" class="com.chenhao.spring.ReplacerChangeMethod"></bean> </beans> //測試方法 public class TestDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("replaced-method.xml"); TestChangeMethod test =(TestChangeMethod) context.getBean("changeMe"); test.changeMe(); } }
接下來我們看下解析replaced-method的方法程式碼:
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) { Element replacedMethodEle = (Element) node; String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE); String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE); ReplaceOverride replaceOverride = new ReplaceOverride(name, callback); // Look for arg-type match elements. List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT); for (Element argTypeEle : argTypeEles) { String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE); match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle)); if (StringUtils.hasText(match)) { replaceOverride.addTypeIdentifier(match); } } replaceOverride.setSource(extractSource(replacedMethodEle)); overrides.addOverride(replaceOverride); } } }
我們可以看到無論是 look-up 還是 replaced-method 是構造了 MethodOverride ,並最終記錄在了 AbstractBeanDefinition 中的 methodOverrides 屬性中
解析constructor-arg
對建構函式的解析式非常常用,也是非常複雜的,我們先從一個簡單配置建構函式的例子開始分析,程式碼如下:
public void parseConstructorArgElement(Element ele, BeanDefinition bd) { //提前index屬性 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); //提前type屬性 String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); //提取name屬性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { this.parseState.push(new ConstructorArgumentEntry(index)); //解析ele對應的元素屬性 Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } else { bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); } } finally { this.parseState.pop(); } } } catch (NumberFormatException ex) { error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); } } else { try { this.parseState.push(new ConstructorArgumentEntry()); Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this.parseState.pop(); } } }
上述程式碼的流程可以簡單的總結為如下:
(1)首先提取index、type、name等屬性
(2)根據是否配置了index屬性解析流程不同
如果配置了index屬性,解析流程如下:
(1)使用parsePropertyValue(ele, bd, null)方法讀取constructor-arg的子元素
(2)使用ConstructorArgumentValues.ValueHolder封裝解析出來的元素
(3)將index、type、name屬性也封裝進ValueHolder中,然後將ValueHoder新增到當前beanDefinition的ConstructorArgumentValues的indexedArgumentValues,而indexedArgumentValues是一個map型別
如果沒有配置index屬性,將index、type、name屬性也封裝進ValueHolder中,然後將ValueHoder新增到當前beanDefinition的ConstructorArgumentValues的genericArgumentValues中
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; // Should only have one child element: ref, value, list, etc. NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //略過description和meta屬性 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { // Child element is what we're looking for. if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); } else { subElement = (Element) node; } } } //解析ref屬性 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); //解析value屬性 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } if (hasRefAttribute) { String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } //使用RuntimeBeanReference來封裝ref對應的bean RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) { //使用TypedStringValue 來封裝value屬性 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } else if (subElement != null) { //解析子元素 return parsePropertySubElement(subElement, bd); } else { // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); return null; } }
上述程式碼的執行邏輯簡單總結為:
(1)首先略過decription和meta屬性
(2)提取constructor-arg上的ref和value屬性,並驗證是否存在
(3)存在ref屬性時,用RuntimeBeanReference來封裝ref
(4)存在value屬性時,用TypedStringValue來封裝
(5)存在子元素時,對於子元素的處理使用了方法parsePropertySubElement(subElement, bd);,其程式碼如下:
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) { return parsePropertySubElement(ele, bd, null); } public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) { //判斷是否是預設標籤處理 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } //對於bean標籤的處理 else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } else if (nodeNameEquals(ele, REF_ELEMENT)) { // A generic reference to any name of any bean. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { // A reference to the id of another bean in a parent context. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean' or 'parent' is required for <ref> element", ele); return null; } } if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } //idref元素處理 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } //value元素處理 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } //null元素處理 else if (nodeNameEquals(ele, NULL_ELEMENT)) { // It's a distinguished null value. Let's wrap it in a TypedStringValue // object in order to preserve the source location. TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } //array元素處理 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } //list元素處理 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } //set元素處理 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } //map元素處理 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } //props元素處理 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
解析子元素properties
對於propertie元素的解析是使用的parsePropertyElements(ele, bd);方法,我們看下其原始碼如下:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { parsePropertyElement((Element) node, bd); } } }
裡面實際的解析是用的parsePropertyElement((Element) node, bd);方法,繼續跟蹤程式碼:
public void parsePropertyElement(Element ele, BeanDefinition bd) { String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { //不允許多次對同一屬性配置 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }
我們看到程式碼的邏輯非常簡單,在獲取了propertie的屬性後使用PropertyValue 進行封裝,然後將其新增到BeanDefinition的propertyValueList中
推薦部落格
解析子元素 qualifier
對於 qualifier 元素的獲取,我們接觸更多的是註解的形式,在使用 Spring 架中進行自動注入時,Spring 器中匹配的候選 Bean 數目必須有且僅有一個,當找不到一個匹配的 Bean 時, Spring容器將丟擲 BeanCreationException 異常, 並指出必須至少擁有一個匹配的 Bean。
Spring 允許我們通過Qualifier 指定注入 Bean的名稱,這樣歧義就消除了,而對於配置方式使用如:
<bean id="myTestBean" class="com.chenhao.spring.MyTestBean">
<qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="gf" /> </bean>
其解析過程與之前大同小異 這裡不再重複敘述
至此我們便完成了對 XML 文件到 GenericBeanDefinition 的轉換, 就是說到這裡, XML 中所有的配置都可以在 GenericBeanDefinition的例項類中應找到對應的配置。
GenericBeanDefinition 只是子類實現,而大部分的通用屬性都儲存在了 bstractBeanDefinition 中,那麼我們再次通過 AbstractBeanDefinition 的屬性來回顧一 下我們都解析了哪些對應的配置。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable { // 此處省略靜態變數以及final變數 @Nullable private volatile Object beanClass; /** * bean的作用範圍,對應bean屬性scope */ @Nullable private String scope = SCOPE_DEFAULT; /** * 是否是抽象,對應bean屬性abstract */ private boolean abstractFlag = false; /** * 是否延遲載入,對應bean屬性lazy-init */ private boolean lazyInit = false; /** * 自動注入模式,對應bean屬性autowire */ private int autowireMode = AUTOWIRE_NO; /** * 依賴檢查,Spring 3.0後棄用這個屬性 */ private int dependencyCheck = DEPENDENCY_CHECK_NONE; /** * 用來表示一個bean的例項化依靠另一個bean先例項化,對應bean屬性depend-on */ @Nullable private String[] dependsOn; /** * autowire-candidate屬性設定為false,這樣容器在查詢自動裝配物件時, * 將不考慮該bean,即它不會被考慮作為其他bean自動裝配的候選者, * 但是該bean本身還是可以使用自動裝配來注入其他bean的 */ private boolean autowireCandidate = true; /** * 自動裝配時出現多個bean候選者時,將作為首選者,對應bean屬性primary */ private boolean primary = false; /** * 用於記錄Qualifier,對應子元素qualifier */ private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(0); @Nullable private Supplier<?> instanceSupplier; /** * 允許訪問非公開的構造器和方法,程式設定 */ private boolean nonPublicAccessAllowed = true; /** * 是否以一種寬鬆的模式解析建構函式,預設為true, * 如果為false,則在以下情況 * interface ITest{} * class ITestImpl implements ITest{}; * class Main { * Main(ITest i) {} * Main(ITestImpl i) {} * } * 丟擲異常,因為Spring無法準確定位哪個建構函式程式設定 */ private boolean lenientConstructorResolution = true; /** * 對應bean屬性factory-bean,用法: * <bean id = "instanceFactoryBean" class = "example.chapter3.InstanceFactoryBean" /> * <bean id = "currentTime" factory-bean = "instanceFactoryBean" factory-method = "createTime" /> */ @Nullable private String factoryBeanName; /** * 對應bean屬性factory-method */ @Nullable private String factoryMethodName; /** * 記錄建構函式注入屬性,對應bean屬性constructor-arg */ @Nullable private ConstructorArgumentValues constructorArgumentValues; /** * 普通屬性集合 */ @Nullable private MutablePropertyValues propertyValues; /** * 方法重寫的持有者,記錄lookup-method、replaced-method元素 */ @Nullable private MethodOverrides methodOverrides; /** * 初始化方法,對應bean屬性init-method */ @Nullable private String initMethodName; /** * 銷燬方法,對應bean屬性destroy-method */ @Nullable private String destroyMethodName; /** * 是否執行init-method,程式設定 */ private boolean enforceInitMethod = true; /** * 是否執行destroy-method,程式設定 */ private boolean enforceDestroyMethod = true; /** * 是否是使用者定義的而不是應用程式本身定義的,建立AOP時候為true,程式設定 */ private boolean synthetic = false; /** * 定義這個bean的應用,APPLICATION:使用者,INFRASTRUCTURE:完全內部使用,與使用者無關, * SUPPORT:某些複雜配置的一部分 * 程式設定 */ private int role = BeanDefinition.ROLE_APPLICATION; /** * bean的描述資訊 */ @Nullable private String description; /** * 這個bean定義的資源 */ @Nullable private Resource resource; }