如何利用Spring把元素解析成BeanDefinition對象
前言
spring中解析元素最重要的一個對象應該就屬于 BeanDefinition了;這個Spring容器中最基本的內(nèi)部數(shù)據(jù)結構;它讓xml文件中所有的配置有了一個歸屬的地方;在xml中存在的配置都能在BeanDefinition找到對應的屬性;我們今天來看看BeanDefinition是如何被創(chuàng)建的
1.BeanDefinition
Spring 容器中的內(nèi)部數(shù)據(jù)結構,是轉換為容器中bean實例的最小接口,其中包含了 屬性值、構造函數(shù)的參數(shù)值等等;
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { /** * 作用域標識符。 */ String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; // singleton作用域 String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // prototype作用域 /** * 角色提示 */ int ROLE_APPLICATION = 0; // 表明BeanDefinition是應用程序的主要部分。通常對應于用戶定義的Bean int ROLE_SUPPORT = 1; // 表明BeanDefinition是支持大部分配置。通常是外部的ComponentDefinition int ROLE_INFRASTRUCTURE = 2; // 表明BeanDefinition提供了一個完全后臺角色,與最終用戶無關。這個提示是在注冊bean時使用的,這些bean完全是組件定義的內(nèi)部工作的一部分 /** * 獲取、設置該BeanDefinition的父Bean的名稱 */ String getParentName(); void setParentName(String parentName); /** * 獲取、設置當前Bean的類名 * */ String getBeanClassName(); // 注意:這并不一定是運行時真正的類名,如果有一個子定義覆蓋/繼承父類的名稱。 void setBeanClassName(String beanClassName); // 注意:類名可以在bean工廠的后處理器中進行修改,通常用它的解析變量替換原來的類名 /** * 獲取、設置工廠Bean或工廠方法(如果有) */ String getFactoryBeanName(); void setFactoryBeanName(String factoryBeanName); String getFactoryMethodName(); void setFactoryMethodName(String factoryMethodName); // 注意:該方法由指定工廠Bean調用或者作為本地bean類的靜態(tài)方法 /** * 獲取、設置當前Bean的作用域或者為null(還不明確時) */ String getScope(); void setScope(String scope); /** * 獲取、設置懶初始化 */ boolean isLazyInit(); void setLazyInit(boolean lazyInit); /** * 獲取、設置該Bean所依賴的Bean名稱列表 */ String[] getDependsOn(); void setDependsOn(String... dependsOn); /** * 獲取、設置該Bean是否為其他Bean自動裝配的候選者 */ boolean isAutowireCandidate void setAutowireCandidate(boolean autowireCandidate); /** * 獲取、設置該Bean是否為自動裝配的主要候選者 */ boolean isPrimary(); void setPrimary(boolean primary); /** * 獲取該Bean的構造器參數(shù)值 */ ConstructorArgumentValues getConstructorArgumentValues(); /** * 獲取該Bean的可運用于新實例的屬性值 */ MutablePropertyValues getPropertyValues(); /** * 判斷該Bean是否為單例、原型、抽象Bean */ boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); /** * 獲取該Bean所扮演的角色(ROLE_APPLICATION、ROLE_SUPPORT、ROLE_INFRASTRUCTURE) */ int getRole(); /** * 獲取該Bean、資源的可讀性描述 */ String getDescription(); String getResourceDescription(); /** * 獲取原始的BeanDefinition */ BeanDefinition getOriginatingBeanDefinition(); }
2.BeanDefinitionParserDelegate
BeanDefinitionParserDelegate是一個非常重要的類;spring將所有的解析操作都委托給了這個類;所有我們今天分析 BeanDefinition的創(chuàng)建,都是在這個類里面;
那么我們進一起進入探個究竟把;創(chuàng)建BeanDefinition的入口
2.1.parseBeanDefinitionElement
/** 解析元素 *1.containingBean 還不清楚作用,回頭分析 TODO... *2.解析 id,name屬性放入List<String> aliases中;aliases持有所有別名 *3.檢查beanName,和aliases是否跟之前有重復;因為當前的對象BeanDefinitionParserDelegate中屬性 Set<String> usedNames 會持有所有解析出來的beanName 和 aliases; *4.解析元素,將xml中的所有元素解析成AbstractBeanDefinition中對應的屬性; *5.解析完了拿到AbstractBeanDefinition的實例之后;創(chuàng)建一個BeanDefinitionHolder實例對象; 這個實例對象持有 AbstractBeanDefinition,beanName,aliases;這些屬性;并且返回holder實例對象; */ public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //獲取id String id = ele.getAttribute(ID_ATTRIBUTE); //獲取name屬性;例如:<bean id="simpleProducer" name="aliases1,;aliases2,;aliases3" > 這樣就設置了多個別名了 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); if (StringUtils.hasLength(nameAttr)) { //解析別名;上面的例子就解析成了 三個別名了aliases1,;aliases2,;aliases3 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; //如果沒有設置id,但是設置了name ;那么就把name別名中的第一個當做 bean的名字;并且從別名中remove掉 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是否唯一 *1.先判斷this.usedNames 是否包含beanName(this.usedNames是一個 set集合,專門保存所有的beanName) *2.如果this.usedNames不包含,再判斷當前aliase是否已經(jīng)被set到this.usedNames集合中了; *3.如果上述beanName或者aliase中被發(fā)現(xiàn)已注冊則拋出異常,否則將beanName和aliase一起add到this.usedNames中去 *4.就算beanName與別名aliase有重復也沒有關系,set會自動去重 */ checkNameUniqueness(beanName, aliases, ele); } /** *1.解析Element,返回解析后的對象 AbstractBeanDefinition;很多屬性例如:class、parent、scope、abstract、lazy-init、meta、lookup-method、replaced-method、constructor-arg、property、qualifier等; *2.這個beanName作為參數(shù)只是用作 異常信息記錄 *3.看下文詳細 */ AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { //如果沒有設置 id 也沒有設置name屬性;則spring生成一個beanName if (!StringUtils.hasText(beanName)) { try { //TODO... 這個containingBean還不知道是干啥用的 if (containingBean != null) { //自動生成beanName;生成規(guī)則我就不贅述了 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); // /** *1.返回一個BeanDefinitionHolder對象,繼承子 implements BeanMetadataElement ;這個對象持有 * this.beanDefinition = beanDefinition; this.beanName = beanName; this.aliases = aliases; */ return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
2.2.parseBeanDefinitionElement
/** * 解析bean definition ,不包括名稱或別名(因為上面已經(jīng)解析過名稱和別名了),當解析異常的時候回返回null; * 返回的是一個GenericBeanDefinition對象 */ public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { /** * 1.BeanEntry是一個 bean的實體類,它implements了 ParseState.Entry接口;Entry是一個標志接口,什么都沒有做; * BeanEntry 只有一個private String beanDefinitionName;屬性 * 2.ParseState類里面有一個private final Stack<Entry> state;棧;存放的對象類型是 Entry * 3.ParseState簡單的棧結構來跟蹤邏輯位置中解析過程,主要是記錄異常信息;在異常的時候 Spring調用error方法,會把ParseState棧中的信息一起拋出 */ this.parseState.push(new BeanEntry(beanName)); //獲取element中有 class屬性; String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { //獲取parent屬性 String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } /** *1.AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable ;它實現(xiàn)了部分BeanDefinition; *2.實現(xiàn)上創(chuàng)建的是一個GenericBeanDefinition對象;這個對象繼承了AbstractBeanDefinition;并且新增了屬性private String parentName;還實現(xiàn)了cloneBeanDefinition()等方法 *3.父類類名路徑,自身全類名路徑,和當前XmlReaderContext的類加載器為參數(shù)創(chuàng)建 AbstractBeanDefinition return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); *4.如果ClassLoader為null;則 bd.setBeanClassName(className);設置全類名路徑; * 如果不為null;則bd.setBeanClass(ClassUtils.forName(className, classLoader));直接利用反射得到類的Class類型;最種調用的是 ClassLoader.loadClass(name) */ AbstractBeanDefinition bd = createBeanDefinition(className, parent); /** *1.解析 <bean >里面的所有屬性值;例如:scope,abstract,lazy-init,autowire,primary,depends-on等 *2.所有的屬性都設置到了 AbstractBeanDefinition bd中 */ parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //設置description值;例如<description>test</description> bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); /** *1.解析元數(shù)據(jù) Meta; Meta在Element中是 <meta key="test" value="testvalue" ></meta> *2.方法參數(shù)類型是 BeanMetadataAttributeAccessor;bd是有繼承BeanMetadataAttributeAccessor的; *3.解析了meta中的key和value;放入到BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);然后bd調用attributeAccessor.addMetadataAttribute(attribute);就不meta元數(shù)據(jù)放入到bd中去了; */ parseMetaElements(ele, bd); /** *重寫特定的方法返回 指定 的實例對象 *1.Spring有一種機制,可以動態(tài)的實現(xiàn)或重寫bean容器中指定bean的指定方法,然后將返回值指定為bean容器中的另一個bean *2.使用的時候 <bean id="beanB" class="com.app.BeanB" scope="prototype"/> <bean id="beanA" class="com.app.BeanA"> <!-- 表示將由Spring重寫getBeanB()方法,并返回名為beanB的bean --> <lookup-method name="getBeanB" bean="beanB"/> </bean> *3.bd中的屬性private MethodOverrides methodOverrides = new MethodOverrides();專門存放這個 *4.解析到bd中 LookupOverride override = new LookupOverride(methodName, beanRef); override.setSource(extractSource(ele)); overrides.addOverride(override); *5.詳細:[這里寫鏈接內(nèi)容](https://blog.csdn.net/elim168/article/details/74939012) */ parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); /** *1.跟上面類似 *2.replaced method注入是spring動態(tài)改變bean里方法的實現(xiàn)。需要改變的方法,使用spring內(nèi)原有其他類(需要繼承接口org.springframework.beans.factory.support.MethodReplacer)的邏輯,替換這個方法。通過改變方法執(zhí)行邏輯來動態(tài)改變方法。內(nèi)部實現(xiàn)為使用cglib方法,重新生成子類,重寫配置的方法和返回對象,達到動態(tài)改變的效果。 3.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); */ parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // /** *1.解析構造函數(shù)中的參數(shù) constructor-arg;放入bd中的 ConstructorArgumentValues constructorArgumentValues屬性中Map<Integer, ValueHolder> indexedArgumentValues map中; *2.解析的是 <constructor-arg index="0" value="1" />這種的 */ parseConstructorArgElements(ele, bd); /** *1.set方法注入的處理(比較常用的注入);格式是<property name="application" value="${myApplicationCode}"/> *2.與上面的構造函數(shù)不一樣的是;這里解析出來的值是個PropertyValue對象 *3.show the code //解析出來屬性值 Object val = parsePropertyValue(ele, bd, propertyName); //構造PropertyValue實例 PropertyValue pv = new PropertyValue(propertyName, val); //解析元數(shù)據(jù) meta數(shù)據(jù) ;屬性下也可以配置meta  parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); //將PropertyValue設置到bd的MutablePropertyValues propertyValues;屬性中; bd.getPropertyValues().addPropertyValue(pv); */ parsePropertyElements(ele, bd); /** *1.解析qualifier元素;存放在 Map<String, AutowireCandidateQualifier> qualifiers */ 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; }
2.3 parseBeanDefinitionAttributes
//解析bean的屬性值 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) { /**singleton 屬性是1.x的時候的東西,現(xiàn)在已經(jīng)廢棄,用scope代替了;例如 scope="singleton"**/ if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } /** *設置作用域;private String scope = SCOPE_DEFAULT;是AbstractBeanDefinition中的屬性,默認是 "" ; */ else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } /** 這個containingBean 暫時不清楚作用,等會分析 TODO.. **/ else if (containingBean != null) { // Take default from containing bean in case of an inner bean definition. bd.setScope(containingBean.getScope()); } //設置 abstract屬性,默認是 false if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } //如果當前元素沒有設置 lazyInit 懶加載;則去 this.defaults.getLazyInit();這個defaults是上一篇分析過的;整個xml文件全局的默認值; String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); // 設置注入方式,有 byName byType 等等,如果當前沒有設置,則取this.defaults.getAutowire()的值; //然后將其轉換為對應的int類型;例如byName是int AUTOWIRE_BY_NAME = 1; String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); //這個dependency-check字面意義是依賴檢查的類型;具體一會分析;如果當前沒有設置,則取this.defaults.getDependencyCheck()的值;TODO... String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE); bd.setDependencyCheck(getDependencyCheck(dependencyCheck)); /** * 1.設置depends-on屬性; * 2.depends-on="commonService,;redisComponent"可以用 ,;來設置多個;所以AbstractBeanDefinition中的dependsOn是一個數(shù)組類型 * 3.depends-on是指指定Bean初始化及銷毀時的順序,使用depends-on屬性指定的Bean要先初始化完畢后才初始化當前Bean */ if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } //這個autowireCandidate不是很清楚 回頭分析TODo.. 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 裝配時當出現(xiàn)多個Bean候選者時,被標記為Primary的Bean將作為首選者,否則將拋出異常 ;默認為false //https://blog.csdn.net/qq_16055765/article/details/78833260 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } //設置初始化方法 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); if (!"".equals(initMethodName)) { bd.setInitMethodName(initMethodName); } } else { //如果 當前元素沒有設置 init_method 屬性,則判斷 xml全局配置有沒有設置;this.defaults是全局設置 if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } } //與上面初始化無二 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); } } //設置factoryMethodName; spring通過工廠方法配置Bean //https://blog.csdn.net/mrwuyi/article/details/51578654 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } //設置factoryBeanName spring通過工廠方法配置Bean //https://blog.csdn.net/mrwuyi/article/details/51578654 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
2.4 parseConstructorArgElement
/** * 解析 構造函數(shù)的參數(shù) */ public void parseConstructorArgElement(Element ele, BeanDefinition bd) { String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); //如果xml配置了index if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { /** *1.構造參數(shù)實體類繼承子ParseState.Entry ConstructorArgumentEntry implements ParseState.Entry;有個int index;屬性 *2.上面解析beanName和aliaes的時候也有個BeanEntry實體類繼承自ParseState.Entry,它有個屬性是beanDefinitionName *3.同樣的,將這個實體類push到 parseState中存放; */ this.parseState.push(new ConstructorArgumentEntry(index)); /** * Get the value of a property element. May be a list etc. * Also used for constructor arguments, "propertyName" being null in this case. * 獲取屬性值 */ Object value = parsePropertyValue(ele, bd, null); //新建一個ValueHolder實例,這個專門存放構造參數(shù)的一些 屬性值的 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); //設置類型 if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } //設置name if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); //驗證index是否設置正確 if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } else { //ConstructorArgumentValues有個 Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<Integer, ValueHolder>(0)來hold參數(shù) 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(); } } }
再著重分析一下Object value = parsePropertyValue(ele, bd, null);
解析屬性值,解析property 或者 constructor-arg;如下:
<bean class="src.HelloWorld"> <property name="beanA" ref="beanA" /> <property name="name" value="姓名" /> <constructor-arg name="a" value="1" /> <constructor-arg name="list"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </constructor-arg> <meta key="meta1" value="meta1value"/> </bean>
- 這個方法只有兩個地方調用 ①.解析屬性property元素的時候parsePropertyValue(ele, bd, propertyName) ②.解析構造參數(shù)constructor-arg元素的時候 parsePropertyValue(ele, bd, null)
- 檢查配置是否正確 最多只能有其中一個元素:ref,value,子元素(ref,value,list,set array 等等形式的元素) ;
- 解析子元素,ref,value,list,set array.....等等
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; // 最多只能有其中一個元素:ref,value,子元素(ref,value,list,set array 等等形式的元素) 等等 /** *<constructor-arg name="a" > *<value >1</value> *</constructor-arg> */ NodeList nl = ele.getChildNodes(); Element subElement = null; /**如果寫法是下面這樣的嵌套子元素的話 就要進入parsePropertySubElement解析嵌套子元素了 *<constructor-arg name="a" > *<value >1</value> *</constructor-arg> */ for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); 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; } } } boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); //1.如果 ref 和 value都存在 拋異常 //2.如果ref或者value存在 一個并且 subElement也存在 拋異常 if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } //如果是ref;返回RuntimeBeanReference implements BeanReference;有beanName,toParent,source等屬性 if (hasRefAttribute) { String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } //如果是value;返回TypedStringValue;設置屬性 value和source else if (hasValueAttribute) { TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } //如果是list;解析一下子元素 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; } } //解析子元素 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { //如果不是默認的命名空間,則最終調用public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) 來解析; //第一篇文章分析過 根據(jù)元素選擇不同的解析器來解析; 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; } //如果是ref 元素 RuntimeBeanReference 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 the same XML file. refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); 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', 'local' 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)) { /**解析 下面這種形式的 * <constructor-arg name="a" > * <value type="java.lang.Integer" >1</value> * </constructor-arg> * <property name="name"> * <value >姓名</value> * </property> * 1.如果沒有設置type屬性,則返回new TypedStringValue(value);SpecifiedTypeName是空串; * 2.如果設置了type,再判斷當前readerContext是否有ClassLoader,如果有,則利用反射生成targetTypeName類型的 Class對象;再返回new TypedStringValue(value, targetType);這個targetType就是Class對象 * 3.否則的話,返回new TypedStringValue(value, targetTypeName);targetType就是全類名路徑 */ 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; } }
3 總結
至此,BeanDefinition就已經(jīng)解析完成了! 我們一起總結一下整個流程:
- 解析 id,name屬性放入List aliases中;aliases持有所有別名
- 檢查beanName,和aliases是否跟之前有重復;因為當前的對象BeanDefinitionParserDelegate中屬性 Set usedNames 會持有所有解析出來的beanName 和 aliases;
- 解析元素,將xml中的所有元素解析成AbstractBeanDefinition中對應的屬性;
- 解析完了拿到AbstractBeanDefinition的實例之后;創(chuàng)建一個BeanDefinitionHolder實例對象; 這個實例對象持有 AbstractBeanDefinition,beanName,aliases;這些屬性;并且返回holder實例對象;
到此這篇關于如何利用Spring把元素解析成BeanDefinition對象的文章就介紹到這了,更多相關Spring BeanDefinition內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
通過springboot+mybatis+druid配置動態(tài)數(shù)據(jù)源
這篇文章主要介紹了通過springboot+mybatis+druid配置動態(tài)數(shù)據(jù)源,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,,需要的朋友可以參考下2019-06-06使用@JsonFormat和@DateTimeFormat對Date格式化操作
這篇文章主要介紹了使用@JsonFormat和@DateTimeFormat對Date格式化操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08