1. 程式人生 > >《Spring技術內幕》學習筆記4——IoC容器解析Bean定義資源並註冊解析後的Bean

《Spring技術內幕》學習筆記4——IoC容器解析Bean定義資源並註冊解析後的Bean

原文連結http://blog.csdn.net/chjttony/article/details/6261708

1.通過前兩篇部落格的分析,我們已經瞭解了Spring IoC容器定位和載入Bean定義資原始檔的基本過程,接下來我們要繼續分析Spring IoC容器將載入的Bean定義資原始檔轉換為Document物件之後,是如何將其解析為Spring IoC管理的Bean物件並將其註冊到容器中的。

2.XmlBeanDefinitionReader解析載入的Bean定義資原始檔:

XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML檔案中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之後將其轉換為Document物件,接下來呼叫registerBeanDefinitions啟動Spring IoC容器對Bean定義的解析過程,registerBeanDefinitions方法原始碼如下:

  1. //按照Spring的Bean語義要求將Bean定義資源解析並轉換為容器內部資料結構  
  2. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {  
  3.         //得到BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析  
  4.         BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();  
  5.         //獲得容器中註冊的Bean數量  
  6.         int countBefore = getRegistry().getBeanDefinitionCount();  
  7. //解析過程入口,這裡使用了委派模式,BeanDefinitionDocumentReader只是個介面,//具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成  
  8.         documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  
  9.         //統計解析的Bean數量  
  10.         return getRegistry().getBeanDefinitionCount() - countBefore;  
  11.     }  
  12. //建立BeanDefinitionDocumentReader物件,解析Document物件  
  13. protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {  
  14.         return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));  
  15.     }  

Bean定義資源的載入解析分為以下兩個過程:

首先,通過呼叫XML解析器將Bean定義資原始檔轉換得到Document物件,但是這些Document物件並沒有按照Spring的Bean規則進行解析。這一步是載入的過程

其次,在完成通用的XML解析之後,按照Spring的Bean規則對Document物件進行解析。

按照Spring的Bean規則對Document物件解析的過程是在介面BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。

BeanDefinitionDocumentReader介面通過registerBeanDefinitions方法呼叫其實現類DefaultBeanDefinitionDocumentReader對Document物件進行解析,解析的程式碼如下:

  1. //根據Spring DTD對Bean的定義規則解析Bean定義Document物件  
  2. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {  
  3.         //獲得XML描述符  
  4.         this.readerContext = readerContext;  
  5.         logger.debug("Loading bean definitions");  
  6.         //獲得Document的根元素  
  7.         Element root = doc.getDocumentElement();  
  8.         //具體的解析過程由BeanDefinitionParserDelegate實現,  
  9. //BeanDefinitionParserDelegate中定義了Spring Bean定義XML檔案的各種元素  
  10.         BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);  
  11.         //在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴充套件性  
  12.         preProcessXml(root);  
  13.         //從Document的根元素開始進行Bean定義的Document物件  
  14.         parseBeanDefinitions(root, delegate);  
  15.         //在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴充套件性  
  16. postProcessXml(root);  
  17.     }  
  18.     //建立BeanDefinitionParserDelegate,用於完成真正的解析過程  
  19.     protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {  
  20.         BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);  
  21.         //BeanDefinitionParserDelegate初始化Document根元素  
  22.         delegate.initDefaults(root);  
  23.         return delegate;  
  24.     }  
  25.     //使用Spring的Bean規則從Document的根元素開始進行Bean定義的Document物件  
  26.     protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {  
  27.         //Bean定義的Document物件使用了Spring預設的XML名稱空間  
  28.         if (delegate.isDefaultNamespace(root)) {  
  29.             //獲取Bean定義的Document物件根元素的所有子節點  
  30.             NodeList nl = root.getChildNodes();  
  31.             for (int i = 0; i < nl.getLength(); i++) {  
  32.                 Node node = nl.item(i);  
  33.                 //獲得Document節點是XML元素節點  
  34.                 if (node instanceof Element) {  
  35.                     Element ele = (Element) node;  
  36.                 //Bean定義的Document的元素節點使用的是Spring預設的XML名稱空間  
  37.                     if (delegate.isDefaultNamespace(ele)) {  
  38.                         //使用Spring的Bean規則解析元素節點  
  39.                         parseDefaultElement(ele, delegate);  
  40.                     }  
  41.                     else {  
  42. //沒有使用Spring預設的XML名稱空間,則使用使用者自定義的解//析規則解析元素節點  
  43.                         delegate.parseCustomElement(ele);  
  44.                     }  
  45.                 }  
  46.             }  
  47.         }  
  48.         else {  
  49.             //Document的根節點沒有使用Spring預設的名稱空間,則使用使用者自定義的  
  50.             //解析規則解析Document根節點  
  51.             delegate.parseCustomElement(root);  
  52.         }  
  53.     }  
  54.     //使用Spring的Bean規則解析Document元素節點  
  55.     private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {  
  56.         //如果元素節點是<Import>匯入元素,進行匯入解析  
  57. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {  
  58.             importBeanDefinitionResource(ele);  
  59.         }  
  60.         //如果元素節點是<Alias>別名元素,進行別名解析  
  61.         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {  
  62.             processAliasRegistration(ele);  
  63.         }  
  64.         //元素節點既不是匯入元素,也不是別名元素,即普通的<Bean>元素,  
  65. //按照Spring的Bean規則解析元素  
  66.         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {  
  67.             processBeanDefinition(ele, delegate);  
  68.         }  
  69.     }  
  70.     //解析<Import>匯入元素,從給定的匯入路徑載入Bean定義資源到Spring IoC容器中  
  71.     protected void importBeanDefinitionResource(Element ele) {  
  72.         //獲取給定的匯入元素的location屬性  
  73. String location = ele.getAttribute(RESOURCE_ATTRIBUTE);  
  74.         //如果匯入元素的location屬性值為空,則沒有匯入任何資源,直接返回  
  75. if (!StringUtils.hasText(location)) {  
  76.             getReaderContext().error("Resource location must not be empty", ele);  
  77.             return;  
  78.         }  
  79.         //使用系統變數值解析location屬性值  
  80.         location = SystemPropertyUtils.resolvePlaceholders(location);  
  81.         Set<Resource> actualResources = new LinkedHashSet<Resource>(4);  
  82. //標識給定的匯入元素的location是否是絕對路徑  
  83.         boolean absoluteLocation = false;  
  84.         try {  
  85.             absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();  
  86.         }  
  87.         catch (URISyntaxException ex) {  
  88.             //給定的匯入元素的location不是絕對路徑  
  89.         }  
  90.         //給定的匯入元素的location是絕對路徑  
  91.         if (absoluteLocation) {  
  92.             try {  
  93.                 //使用資源讀入器載入給定路徑的Bean定義資源  
  94.                 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);  
  95.                 if (logger.isDebugEnabled()) {  
  96.                     logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");  
  97.                 }  
  98.             }  
  99.             catch (BeanDefinitionStoreException ex) {  
  100.                 getReaderContext().error(  
  101.                         "Failed to import bean definitions from URL location [" + location + "]", ele, ex);  
  102.             }  
  103.         }  
  104.         else {  
  105.             //給定的匯入元素的location是相對路徑  
  106.             try {  
  107.                 int importCount;  
  108.                 //將給定匯入元素的location封裝為相對路徑資源  
  109.                 Resource relativeResource = getReaderContext().getResource().createRelative(location);  
  110.                 //封裝的相對路徑資源存在  
  111.                 if (relativeResource.exists()) {  
  112.                     //使用資源讀入器載入Bean定義資源  
  113.                     importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);  
  114.                     actualResources.add(relativeResource);  
  115.                 }  
  116.                 //封裝的相對路徑資源不存在  
  117.                 else {  
  118.                     //獲取Spring IoC容器資源讀入器的基本路徑  
  119.                     String baseLocation = getReaderContext().getResource().getURL().toString();  
  120.                     //根據Spring IoC容器資源讀入器的基本路徑載入給定匯入  
  121.                     //路徑的資源  
  122.                     importCount = getReaderContext().getReader().loadBeanDefinitions(  
  123.                             StringUtils.applyRelativePath(baseLocation, location), actualResources);  
  124.                 }  
  125.                 if (logger.isDebugEnabled()) {  
  126.                     logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");  
  127.                 }  
  128.             }  
  129.             catch (IOException ex) {  
  130.                 getReaderContext().error("Failed to resolve current resource location", ele, ex);  
  131.             }  
  132.             catch (BeanDefinitionStoreException ex) {  
  133.                 getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",  
  134.                         ele, ex);  
  135.             }  
  136.         }  
  137.         Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);  
  138.         //在解析完<Import>元素之後,傳送容器匯入其他資源處理完成事件  
  139.         getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));  
  140.     }  
  141.     //解析<Alias>別名元素,為Bean向Spring IoC容器註冊別名  
  142.     protected void processAliasRegistration(Element ele) {  
  143.         //獲取<Alias>別名元素中name的屬性值  
  144. String name = ele.getAttribute(NAME_ATTRIBUTE);  
  145. //獲取<Alias>別名元素中alias的屬性值  
  146.         String alias = ele.getAttribute(ALIAS_ATTRIBUTE);  
  147.         boolean valid = true;  
  148.         //<alias>別名元素的name屬性值為空  
  149. if (!StringUtils.hasText(name)) {  
  150.             getReaderContext().error("Name must not be empty", ele);  
  151.             valid = false;  
  152.         }  
  153.         //<alias>別名元素的alias屬性值為空  
  154.         if (!StringUtils.hasText(alias)) {  
  155.             getReaderContext().error("Alias must not be empty", ele);  
  156.             valid = false;  
  157.         }  
  158.         if (valid) {  
  159.             try {  
  160.                 //向容器的資源讀入器註冊別名  
  161.                 getReaderContext().getRegistry().registerAlias(name, alias);  
  162.             }  
  163.             catch (Exception ex) {  
  164.                 getReaderContext().error("Failed to register alias '" + alias +  
  165.                         "' for bean with name '" + name + "'", ele, ex);  
  166.             }  
  167.             //在解析完<Alias>元素之後,傳送容器別名處理完成事件  
  168.             getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));  
  169.         }  
  170.     }  
  171.     //解析Bean定義資源Document物件的普通元素  
  172.     protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {  
  173.         // BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類  
  174. //對Document物件中<Bean>元素的解析由BeanDefinitionParserDelegate實現  BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  
  175.         if (bdHolder != null) {  
  176.             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  
  177.             try {  
  178.     //向Spring IoC容器註冊解析得到的Bean定義,這是Bean定義向IoC容器註冊的入口            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  
  179.             }  
  180.             catch (BeanDefinitionStoreException ex) {  
  181.                 getReaderContext().error("Failed to register bean definition with name '" +  
  182.                         bdHolder.getBeanName() + "'", ele, ex);  
  183.             }  
  184.             //在完成向Spring IoC容器註冊解析得到的Bean定義之後,傳送註冊事件  
  185.             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  
  186.         }  
  187.     }  

通過上述Spring IoC容器對載入的Bean定義Document解析可以看出,我們使用Spring時,在Spring配置檔案中可以使用<Import>元素來匯入IoC容器所需要的其他資源,Spring IoC容器在解析時會首先將指定匯入的資源載入進容器中。使用<Ailas>別名時,Spring IoC容器首先將別名元素所定義的別名註冊到容器中。

對於既不是<Import>元素,又不是<Alias>元素的元素,即Spring配置檔案中普通的<Bean>元素的解析由BeanDefinitionParserDelegate類的parseBeanDefinitionElement方法來實現。

Bean定義資原始檔中的<Import>和<Alias>元素解析在DefaultBeanDefinitionDocumentReader中已經完成,對Bean定義資原始檔中使用最多的<Bean>元素交由BeanDefinitionParserDelegate來解析,其解析實現的原始碼如下:

  1. //解析<Bean>元素的入口  
  2. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {  
  3.         return parseBeanDefinitionElement(ele, null);  
  4.     }  
  5. //解析Bean定義資原始檔中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name  
  6. //和別名屬性  
  7.     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {  
  8.         //獲取<Bean>元素中的id屬性值  
  9.         String id = ele.getAttribute(ID_ATTRIBUTE);  
  10.         //獲取<Bean>元素中的name屬性值  
  11.         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  
  12.         ////獲取<Bean>元素中的alias屬性值  
  13.         List<String> aliases = new ArrayList<String>();  
  14.         //將<Bean>元素中的所有name屬性值存放到別名中  
  15. if (StringUtils.hasLength(nameAttr)) {  
  16.             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);  
  17.             aliases.addAll(Arrays.asList(nameArr));  
  18.         }  
  19.         String beanName = id;  
  20.         //如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName  
  21.         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {  
  22.             beanName = aliases.remove(0);  
  23.             if (logger.isDebugEnabled()) {  
  24.                 logger.debug("No XML 'id' specified - using '" + beanName +  
  25.                         "' as bean name and " + aliases + " as aliases");  
  26.             }  
  27.         }  
  28.         //檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean>  
  29.         //元素中是否包含子<Bean>元素  
  30.         if (containingBean == null) {  
  31.             //檢查<Bean>元素所配置的id、name或者別名是否重複  
  32.             checkNameUniqueness(beanName, aliases, ele);  
  33.         }  
  34.         //詳細對<Bean>元素中配置的Bean定義進行解析的地方  
  35.         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);  
  36.         if (beanDefinition != null) {  
  37.             if (!StringUtils.hasText(beanName)) {  
  38.                 try {  
  39.                     if (containingBean != null) {  
  40. //如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子//<Bean>元素,為解析的Bean生成一個唯一beanName並註冊  
  41.                         beanName = BeanDefinitionReaderUtils.generateBeanName(  
  42.                                 beanDefinition, this.readerContext.getRegistry(), true);  
  43.                     }  
  44.                     else {  
  45. //如果<Bean>元素中沒有配置id、別名或者name,且包含了子//<Bean>元素,為解析的Bean使用別名向IoC容器註冊  
  46.                         beanName = this.readerContext.generateBeanName(beanDefinition);  
  47.                         //為解析的Bean使用別名註冊時,為了向後相容                                    //Spring1.2/2.0,給別名新增類名字尾  
  48.                         String beanClassName = beanDefinition.getBeanClassName();  
  49.                         if (beanClassName != null &&  
  50.                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&  
  51.                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {  
  52.                             aliases.add(beanClassName);  
  53.                         }  
  54.                     }  
  55.                     if (logger.isDebugEnabled()) {  
  56.                         logger.debug("Neither XML 'id' nor 'name' specified - " +  
  57.                                 "using generated bean name [" + beanName + "]");  
  58.                     }  
  59.                 }  
  60.                 catch (Exception ex) {  
  61.                     error(ex.getMessage(), ele);  
  62.                     return null;  
  63.                 }  
  64.             }  
  65.             String[] aliasesArray = StringUtils.toStringArray(aliases);  
  66.             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);  
  67.         }  
  68.         //當解析出錯時,返回null  
  69.         return null;  
  70.     }  
  71. //詳細對<Bean>元素中配置的Bean定義其他屬性進行解析,由於上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其他屬性資料  
  72. public AbstractBeanDefinition parseBeanDefinitionElement(  
  73.             Element ele, String beanName, BeanDefinition containingBean) {  
  74.         //記錄解析的<Bean>  
  75.         this.parseState.push(new BeanEntry(beanName));  
  76. //這裡只讀取<Bean>元素中配置的class名字,然後載入到BeanDefinition中去  
  77.         //只是記錄配置的class名字,不做例項化,物件的例項化在依賴注入時完成  
  78.         String className = null;  
  79.         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {  
  80.             className = ele.getAttribute(CLASS_ATTRIBUTE).trim();  
  81.         }  
  82.         try {  
  83.             String parent = null;  
  84.             //如果<Bean>元素中配置了parent屬性,則獲取parent屬性的值  
  85.             if (ele.hasAttribute(PARENT_ATTRIBUTE)) {  
  86.                 parent = ele.getAttribute(PARENT_ATTRIBUTE);  
  87.             }  
  88.             //根據<Bean>元素配置的class名稱和parent屬性值建立BeanDefinition  
  89.             //為載入Bean定義資訊做準備  
  90.             AbstractBeanDefinition bd = createBeanDefinition(className, parent);  
  91. //對當前的<Bean>元素中配置的一些屬性進行解析和設定,如配置的單態(singleton)屬性等  
  92.             parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);  
  93.     //為<Bean>元素解析的Bean設定description資訊 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));  
  94.             //對<Bean>元素的meta(元資訊)屬性解析  
  95.             parseMetaElements(ele, bd);  
  96.             //對<Bean>元素的lookup-method屬性解析  
  97.             parseLookupOverrideSubElements(ele, bd.getMethodOverrides());  
  98.             //對<Bean>元素的replaced-method屬性解析  
  99.             parseReplacedMethodSubElements(ele, bd.getMethodOverrides());  
  100.             //解析<Bean>元素的構造方法設定  
  101.             parseConstructorArgElements(ele, bd);  
  102.             //解析<Bean>元素的<property>設定  
  103.             parsePropertyElements(ele, bd);  
  104.             //解析<Bean>元素的qualifier屬性  
  105.             parseQualifierElements(ele, bd);  
  106.             //為當前解析的Bean設定所需的資源和依賴物件  
  107.             bd.setResource(this.readerContext.getResource());  
  108.             bd.setSource(extractSource(ele));  
  109.             return bd;  
  110.         }  
  111.         catch (ClassNotFoundException ex) {  
  112.             error("Bean class [" + className + "] not found", ele, ex);  
  113.         }  
  114.         catch (NoClassDefFoundError err) {  
  115.             error("Class that bean class [" + className + "] depends on not found", ele, err);  
  116.         }  
  117.         catch (Throwable ex) {  
  118.             error("Unexpected failure during bean definition parsing", ele, ex);  
  119.         }  
  120.         finally {  
  121.             this.parseState.pop();  
  122.         }  
  123.         //解析<Bean>元素出錯時,返回null  
  124.         return null;  
  125.     }  

只要使用過Spring,對Spring配置檔案比較熟悉的人,通過對上述原始碼的分析,就會明白我們在Spring配置檔案中<Bean>元素的中配置的屬性就是通過該方法解析和設定到Bean中去的。

注意:在解析<Bean>元素過程中沒有建立和例項化Bean物件,只是建立了Bean物件的定義類BeanDefinition,將<Bean>元素中的配置資訊設定到BeanDefinition中作為記錄,當依賴注入時才使用這些記錄資訊建立和例項化具體的Bean物件。

上面方法中一些對一些配置如元資訊(meta)、qualifier等的解析,我們在Spring中配置時使用的也不多,我們在使用Spring的<Bean>元素時,配置最多的是<property>屬性,因此我們下面繼續分析原始碼,瞭解Bean的屬性在解析時是如何設定的。

5.BeanDefinitionParserDelegate解析<property>元素:

BeanDefinitionParserDelegate在解析<Bean>呼叫parsePropertyElements方法解析<Bean>元素中的<property>屬性子元素,解析原始碼如下:

  1. //解析<Bean>元素中的<property>子元素  
  2. public void parsePropertyElements(Element beanEle, BeanDefinition bd) {  
  3.             //獲取<Bean>元素中所有的子元素  
  4. NodeList nl = beanEle.getChildNodes();  
  5.         for (int i = 0; i < nl.getLength(); i++) {  
  6.             Node node = nl.item(i);  
  7.             //如果子元素是<property>子元素,則呼叫解析<property>子元素方法解析  
  8.             if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {  
  9.                 parsePropertyElement((Element) node, bd);  
  10.             }  
  11.         }  
  12.     }  
  13. //解析<property>元素  
  14. public void parsePropertyElement(Element ele, BeanDefinition bd) {  
  15.         //獲取<property>元素的名字   
  16.         String propertyName = ele.getAttribute(NAME_ATTRIBUTE);  
  17.         if (!StringUtils.hasLength(propertyName)) {  
  18.             error("Tag 'property' must have a 'name' attribute", ele);  
  19.             return;  
  20.         }  
  21.         this.parseState.push(new PropertyEntry(propertyName));  
  22.         try {  
  23.             //如果一個Bean中已經有同名的property存在,則不進行解析,直接返回。  
  24.             //即如果在同一個Bean中配置同名的property,則只有第一個起作用  
  25.             if (bd.getPropertyValues().contains(propertyName)) {  
  26.                 error("Multiple 'property' definitions for property '" + propertyName + "'", ele);  
  27.                 return;  
  28.             }  
  29.             //解析獲取property的值  
  30.             Object val = parsePropertyValue(ele, bd, propertyName);  
  31.             //根據property的名字和值建立property例項  
  32.             PropertyValue pv = new PropertyValue(propertyName, val);  
  33.             //解析<property>元素中的屬性  
  34.             parseMetaElements(ele, pv);  
  35.             pv.setSource(extractSource(ele));  
  36.             bd.getPropertyValues().addPropertyValue(pv);  
  37.         }  
  38.         finally {  
  39.             this.parseState.pop();  
  40.         }  
  41.     }  
  42. //解析獲取property值  
  43. public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {  
  44.         String elementName = (propertyName != null) ?  
  45.                         "<property> element for property '" + propertyName + "'" :  
  46.                         "<constructor-arg> element";  
  47.         //獲取<property>的所有子元素,只能是其中一種型別:ref,value,list等  
  48.         NodeList nl = ele.getChildNodes();  
  49.         Element subElement = null;  
  50.         for (int i = 0; i < nl.getLength(); i++) {  
  51.             No