spring加载xml配置文件

最近在看Spring源代码,对配置文件信息的载入是使用Spring的第一步 ,而这第一步就是一个非常复杂的过程.... 
Spring通过定义BeanDefination来管理Ioc中的各种对象以及它们之间的依赖关系,所以载入的过程其实就是将XML文件读取并解析成BeanDefination数据的过程。
我们以最常使用的ClassPathXmlApplicationContext为切入点 

1. 创建一个ClassPathXmlApplicationContext对象,传入文件路径

  

Java代码  收藏代码
  1. ClassPathXmlApplicationContext re = new ClassPathXmlApplicationContext("applicationContext.xml");  

这个构造方法会重载到

Java代码  收藏代码
  1. public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  
  2.         throws BeansException {  
  3.   
  4.     super(parent);  
  5.     setConfigLocations(configLocations);  
  6.     if (refresh) {  
  7.         refresh();  
  8.     }  
  9. }  

 

其中首先设置配置路径 setConfigLocations(configLocations)  ,而后进行刷新 refresh(), 而这个refresh()方法是Ioc容器初始化的入口

2.refresh方法的结构

 refresh方法由AbstractApplicationContext实现

Java代码  收藏代码
  1. public void refresh() throws BeansException, IllegalStateException {  
  2.         synchronized (this.startupShutdownMonitor) {  
  3.             // Prepare this context for refreshing.  
  4.             prepareRefresh();  
  5.   
  6.             // Tell the subclass to refresh the internal bean factory.  
  7.             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  8.   
  9.             // Prepare the bean factory for use in this context.  
  10.             prepareBeanFactory(beanFactory);  
  11.   
  12.             try {  
  13.                 // Allows post-processing of the bean factory in context subclasses.  
  14.                 postProcessBeanFactory(beanFactory);  
  15.   
  16.                 // Invoke factory processors registered as beans in the context.  
  17.                 invokeBeanFactoryPostProcessors(beanFactory);  
  18.   
  19.                 // Register bean processors that intercept bean creation.  
  20.                 registerBeanPostProcessors(beanFactory);  
  21.   
  22.                 // Initialize message source for this context.  
  23.                 initMessageSource();  
  24.   
  25.                 // Initialize event multicaster for this context.  
  26.                 initApplicationEventMulticaster();  
  27.   
  28.                 // Initialize other special beans in specific context subclasses.  
  29.                 onRefresh();  
  30.   
  31.                 // Check for listener beans and register them.  
  32.                 registerListeners();  
  33.   
  34.                 // Instantiate all remaining (non-lazy-init) singletons.  
  35.                 finishBeanFactoryInitialization(beanFactory);  
  36.   
  37.                 // Last step: publish corresponding event.  
  38.                 finishRefresh();  
  39.             }  
  40.   
  41.             catch (BeansException ex) {  
  42.                 // Destroy already created singletons to avoid dangling resources.  
  43.                 destroyBeans();  
  44.   
  45.                 // Reset 'active' flag.  
  46.                 cancelRefresh(ex);  
  47.   
  48.                 // Propagate exception to caller.  
  49.                 throw ex;  
  50.             }  
  51.         }  
  52.     }  

 

 这个方法中描述了ApplicationContext的整个初始化过程,包括BeanFactory的更新,还有messagesource以及一些生命周期有关属性的注册,而我们关心的是BeanFactory的更新,即obtainFreshBeanFactory()方法

 

3.启动对BeanDefination的载入

还是在ApplicationContext类中

Java代码  收藏代码
  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
  2.     refreshBeanFactory();  
  3.     ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
  4.     if (logger.isDebugEnabled()) {  
  5.         logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);  
  6.     }  
  7.     return beanFactory;  
  8. }  

 它的第一步交给一个抽象方法refreshBeanFactory(), 具体的实现在AbstractRefreshableApplicationContext类中

 

Java代码  收藏代码
  1. @Override  
  2. protected final void refreshBeanFactory() throws BeansException {  
  3.     if (hasBeanFactory()) {  
  4.         destroyBeans();  
  5.         closeBeanFactory();  
  6.     }  
  7.     try {  
  8.         DefaultListableBeanFactory beanFactory = createBeanFactory();  
  9.         beanFactory.setSerializationId(getId());  
  10.         customizeBeanFactory(beanFactory);  
  11.         loadBeanDefinitions(beanFactory);  
  12.         synchronized (this.beanFactoryMonitor) {  
  13.             this.beanFactory = beanFactory;  
  14.         }  
  15.     }  
  16.     catch (IOException ex) {  
  17.         throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  
  18.     }  
  19. }  

第一步是判断是否已经创建过BeanFactory,如果是,将它销毁,重新创建
第二步就是创建各种ApplicationContext持有的真正容器实现类DefaultListableBeanFactory,创建Ioc容器

最后启动BeanDefination的载入  loadBeanDefinitions(beanFactory)方法

 

4.BeanFactory将载入工作交给BeanDefinationReader

loadBeanDefinitions(beanFactory)方法是抽象的,又因为我们的配置文件是XML格式的,所以具体实现实在AbstractXmlApplicationConext中

Java代码  收藏代码
  1. @Override  
  2. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
  3.     // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  4.     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  5.   
  6.     // Configure the bean definition reader with this context's  
  7.     // resource loading environment.  
  8.     beanDefinitionReader.setResourceLoader(this);  
  9.     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  10.   
  11.     // Allow a subclass to provide custom initialization of the reader,  
  12.     // then proceed with actually loading the bean definitions.  
  13.     initBeanDefinitionReader(beanDefinitionReader);  
  14.     loadBeanDefinitions(beanDefinitionReader);  
  15. }  

 
这里创建了一个XmlBeanDefinitionReader 对象,它专门用来读取基于XML文件格式的BeanDefinition配置,接下来重载到loadBeanDefinitions(beanDefinitionReader);

Java代码  收藏代码
  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  
  2.     Resource[] configResources = getConfigResources();  
  3.     if (configResources != null) {  
  4.         reader.loadBeanDefinitions(configResources);  
  5.     }  
  6.     String[] configLocations = getConfigLocations();  
  7.     if (configLocations != null) {  
  8.         reader.loadBeanDefinitions(configLocations);  
  9.     }  
  10. }  

 

 首先载入Resource对象用来定位资源,Resource对象的生成在ClassPathXmlApplicationContext 中setConfigLocations(configLocations)方法实现

然后调用XmlBeanDefinitionReader基类AbstractBeanDefinitionReader的loadBeanDefinitions方法

Java代码  收藏代码
  1. public int loadBeanDefinitions(Resource[] resources) throws BeanDefinitionStoreException {  
  2.     Assert.notNull(resources, "Resource array must not be null");  
  3.     int counter = 0;  
  4.     for (Resource resource : resources) {  
  5.         counter += loadBeanDefinitions(resource);  
  6.     }  
  7.     return counter;  
  8. }  

 

然后调用loadBeanDefinitions(resource)方法,此方法的具体实现在XmlBeanDefinitionReader 中

Java代码  收藏代码
  1. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  
  2.     return loadBeanDefinitions(new EncodedResource(resource));  
  3. }  

重载到

Java代码  收藏代码
  1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
  2.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
  3.         if (logger.isInfoEnabled()) {  
  4.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  5.         }  
  6.   
  7.         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
  8.         if (currentResources == null) {  
  9.             currentResources = new HashSet<EncodedResource>(4);  
  10.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
  11.         }  
  12.         if (!currentResources.add(encodedResource)) {  
  13.             throw new BeanDefinitionStoreException(  
  14.                     "Detected recursive loading of " + encodedResource + " - check your import definitions!");  
  15.         }  
  16.         try {  
  17.             InputStream inputStream = encodedResource.getResource().getInputStream();  
  18.             try {  
  19.                 InputSource inputSource = new InputSource(inputStream);  
  20.                 if (encodedResource.getEncoding() != null) {  
  21.                     inputSource.setEncoding(encodedResource.getEncoding());  
  22.                 }  
  23.                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
  24.             }  
  25.             finally {  
  26.                 inputStream.close();  
  27.             }  
  28.         }  
  29.         catch (IOException ex) {  
  30.             throw new BeanDefinitionStoreException(  
  31.                     "IOException parsing XML document from " + encodedResource.getResource(), ex);  
  32.         }  
  33.         finally {  
  34.             currentResources.remove(encodedResource);  
  35.             if (currentResources.isEmpty()) {  
  36.                 this.resourcesCurrentlyBeingLoaded.set(null);  
  37.             }  
  38.         }  
  39.     }  

 

 

在此类中主要是对输入流进行编码操作,然后调用doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法

 

5.XmlBeanDefinitionReader将载入工作交给W3C的dom

因为读入的文件是XML格式的,所以底层的实现肯定是要和W3C的dom结构打交道

doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法正式引入dom

Java代码  收藏代码
  1. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
  2.             throws BeanDefinitionStoreException {  
  3.         try {  
  4.             int validationMode = getValidationModeForResource(resource);  
  5.             Document doc = this.documentLoader.loadDocument(  
  6.                     inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
  7.             return registerBeanDefinitions(doc, resource);  
  8.         }  
  9.         catch (BeanDefinitionStoreException ex) {  
  10.             throw ex;  
  11.         }  
  12.         catch (SAXParseException ex) {  
  13.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  14.                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);  
  15.         }  
  16.         catch (SAXException ex) {  
  17.             throw new XmlBeanDefinitionStoreException(resource.getDescription(),  
  18.                     "XML document from " + resource + " is invalid", ex);  
  19.         }  
  20.         catch (ParserConfigurationException ex) {  
  21.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  22.                     "Parser configuration exception parsing XML from " + resource, ex);  
  23.         }  
  24.         catch (IOException ex) {  
  25.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  26.                     "IOException parsing XML document from " + resource, ex);  
  27.         }  
  28.         catch (Throwable ex) {  
  29.             throw new BeanDefinitionStoreException(resource.getDescription(),  
  30.                     "Unexpected exception parsing XML document from " + resource, ex);  
  31.         }  
  32.     }  

 
在此方法中生成了Document类的对象,下一步是进行对象的注册,registerBeanDefinitions(doc, resource)方法

Java代码  收藏代码
  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {  
  2.     // Read document based on new BeanDefinitionDocumentReader SPI.  
  3.     BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();  
  4.     int countBefore = getRegistry().getBeanDefinitionCount();  
  5.     documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  
  6.     return getRegistry().getBeanDefinitionCount() - countBefore;  
  7. }  

 

此方法统计了注册的BeanDefinition的个数,返回一个int值,而具体的注册工作在BeanDefinitionDocumentReader 接口的实现类DefaultBeanDefinitionDocumentReader 中registerBeanDefinitions(doc, createReaderContext(resource))方法实现

 

6.BeanDefinitionDocumentReader将载入工作交给代理类BeanDefinationParserDelegate

DefaultBeanDefinitionDocumentReader的registerBeanDefinitions(doc, createReaderContext(resource))方法

Java代码  收藏代码
  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {  
  2.     this.readerContext = readerContext;  
  3.   
  4.     logger.debug("Loading bean definitions");  
  5.     Element root = doc.getDocumentElement();  
  6.   
  7.     BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);  
  8.   
  9.     preProcessXml(root);  
  10.     parseBeanDefinitions(root, delegate);  
  11.     postProcessXml(root);  
  12. }  

 

其中首先得到dom结构的根,然后由根进行分析

然后引入BeanDefinitionParserDelegate 代理类对dom结构进行分析,调用parseBeanDefinitions(root, delegate)方法

Java代码  收藏代码
  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {  
  2.     if (delegate.isDefaultNamespace(delegate.getNamespaceURI(root))) {  
  3.         NodeList nl = root.getChildNodes();  
  4.         for (int i = 0; i < nl.getLength(); i++) {  
  5.             Node node = nl.item(i);  
  6.             if (node instanceof Element) {  
  7.                 Element ele = (Element) node;  
  8.                 String namespaceUri = delegate.getNamespaceURI(ele);  
  9.                 if (delegate.isDefaultNamespace(namespaceUri)) {  
  10.                     parseDefaultElement(ele, delegate);  
  11.                 }  
  12.                 else {  
  13.                     delegate.parseCustomElement(ele);  
  14.                 }  
  15.             }  
  16.         }  
  17.     }  
  18.     else {  
  19.         delegate.parseCustomElement(root);  
  20.     }  
  21. }  

 其中最主要的操作是在调用parseDefaultElement(ele, delegate)方法中进行

Java代码  收藏代码
  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {  
  2.     if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {  
  3.         importBeanDefinitionResource(ele);  
  4.     }  
  5.     else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {  
  6.         processAliasRegistration(ele);  
  7.     }  
  8.     else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {  
  9.         processBeanDefinition(ele, delegate);  
  10.     }  
  11. }  

 

从这个方法里面我们就能看出来底层元素的端倪了,首先判断Node是否为import节点,然后是alias节点,最后是bean节点,我们关心的是bean节点,processBeanDefinition(ele, delegate)方法

Java代码  收藏代码
  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {  
  2.     BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  
  3.     if (bdHolder != null) {  
  4.         bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  
  5.         try {  
  6.             // Register the final decorated instance.  
  7.             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  
  8.         }  
  9.         catch (BeanDefinitionStoreException ex) {  
  10.             getReaderContext().error("Failed to register bean definition with name '" +  
  11.                     bdHolder.getBeanName() + "'", ele, ex);  
  12.         }  
  13.         // Send registration event.  
  14.         getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  
  15.     }  
  16. }  

 这里首先由BeanDefinitionParserDelegate 生成BeanDefination的包装类BeanDefinitionHolder ,然后再进行一些修饰工作,这里把工作正式交给BeanDefinitionParserDelegate

 

7.BeanDefinitionParserDelegate 中的解析工作

BeanDefinition的解析主要在BeanDefinitionParserDelegate 的parseBeanDefinitionElement(ele)方法中进行,重载到

Java代码  收藏代码
  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {  
  2.     String id = ele.getAttribute(ID_ATTRIBUTE);  
  3.     String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  
  4.   
  5.     List<String> aliases = new ArrayList<String>();  
  6.     if (StringUtils.hasLength(nameAttr)) {  
  7.         String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);  
  8.         aliases.addAll(Arrays.asList(nameArr));  
  9.     }  
  10.   
  11.     String beanName = id;  
  12.     if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {  
  13.         beanName = aliases.remove(0);  
  14.         if (logger.isDebugEnabled()) {  
  15.             logger.debug("No XML 'id' specified - using '" + beanName +  
  16.                     "' as bean name and " + aliases + " as aliases");  
  17.         }  
  18.     }  
  19.   
  20.     if (containingBean == null) {  
  21.         checkNameUniqueness(beanName, aliases, ele);  
  22.     }  
  23.   
  24.     AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);  
  25.     if (beanDefinition != null) {  
  26.         if (!StringUtils.hasText(beanName)) {  
  27.             try {  
  28.                 if (containingBean != null) {  
  29.                     beanName = BeanDefinitionReaderUtils.generateBeanName(  
  30.                             beanDefinition, this.readerContext.getRegistry(), true);  
  31.                 }  
  32.                 else {  
  33.                     beanName = this.readerContext.generateBeanName(beanDefinition);  
  34.                     // Register an alias for the plain bean class name, if still possible,  
  35.                     // if the generator returned the class name plus a suffix.  
  36.                     // This is expected for Spring 1.2/2.0 backwards compatibility.  
  37.                     String beanClassName = beanDefinition.getBeanClassName();  
  38.                     if (beanClassName != null &&  
  39.                             beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&  
  40.                             !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {  
  41.                         aliases.add(beanClassName);  
  42.                     }  
  43.                 }  
  44.                 if (logger.isDebugEnabled()) {  
  45.                     logger.debug("Neither XML 'id' nor 'name' specified - " +  
  46.                             "using generated bean name [" + beanName + "]");  
  47.                 }  
  48.             }  
  49.             catch (Exception ex) {  
  50.                 error(ex.getMessage(), ele);  
  51.                 return null;  
  52.             }  
  53.         }  
  54.         String[] aliasesArray = StringUtils.toStringArray(aliases);  
  55.         return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);  
  56.     }  
  57.   
  58.     return null;  
  59. }  

 这个方法首先得到元素的name和id以及别名属性,然后再生成底层的AbstractBeanDefinition对象将它们包装生成BeanDefinitionHolder,其中包括bean的名称,别名,以及BeanDefinition,返回给上层方法

核心在于生成BeanDefinition的parseBeanDefinitionElement(ele, beanName, containingBean)方法

Java代码  收藏代码
  1. public AbstractBeanDefinition parseBeanDefinitionElement(  
  2.         Element ele, String beanName, BeanDefinition containingBean) {  
  3.   
  4.     this.parseState.push(new BeanEntry(beanName));  
  5.   
  6.     String className = null;  
  7.     if (ele.hasAttribute(CLASS_ATTRIBUTE)) {  
  8.         className = ele.getAttribute(CLASS_ATTRIBUTE).trim();  
  9.     }  
  10.   
  11.     try {  
  12.         String parent = null;  
  13.         if (ele.hasAttribute(PARENT_ATTRIBUTE)) {  
  14.             parent = ele.getAttribute(PARENT_ATTRIBUTE);  
  15.         }  
  16.         AbstractBeanDefinition bd = createBeanDefinition(className, parent);  
  17.   
  18.         parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);  
  19.         bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));  
  20.   
  21.         parseMetaElements(ele, bd);  
  22.         parseLookupOverrideSubElements(ele, bd.getMethodOverrides());  
  23.         parseReplacedMethodSubElements(ele, bd.getMethodOverrides());  
  24.   
  25.         parseConstructorArgElements(ele, bd);  
  26.         parsePropertyElements(ele, bd);  
  27.         parseQualifierElements(ele, bd);  
  28.   
  29.         bd.setResource(this.readerContext.getResource());  
  30.         bd.setSource(extractSource(ele));  
  31.   
  32.         return bd;  
  33.     }  
  34.     catch (ClassNotFoundException ex) {  
  35.         error("Bean class [" + className + "] not found", ele, ex);  
  36.     }  
  37.     catch (NoClassDefFoundError err) {  
  38.         error("Class that bean class [" + className + "] depends on not found", ele, err);  
  39.     }  
  40.     catch (Throwable ex) {  
  41.         error("Unexpected failure during bean definition parsing", ele, ex);  
  42.     }  
  43.     finally {  
  44.         this.parseState.pop();  
  45.     }  
  46.   
  47.     return null;  
  48. }  

 

 

这个方法我们看起来一目了然,全部都是bean节点中的配置信息

首先得到class的名字,然后得到继承的parent的名字,然后是meta节点,look-up节点,replaced-method节点,构造函数设置节点,最后是比较复杂的property节点,我们继续分析比较复杂的property节点的解析,parsePropertyElements(ele, bd)方法

Java代码  收藏代码
  1. public void parsePropertyElements(Element beanEle, BeanDefinition bd) {  
  2.     NodeList nl = beanEle.getChildNodes();  
  3.     for (int i = 0; i < nl.getLength(); i++) {  
  4.         Node node = nl.item(i);  
  5.         if (node instanceof Element && nodeNameEquals(node, PROPERTY_ELEMENT)) {  
  6.             parsePropertyElement((Element) node, bd);  
  7.         }  
  8.     }  
  9. }  

 将bean节点的子元素逐个取出判断是否为property节点,然后进行解析,parsePropertyElement((Element) node, bd)方法

Java代码  收藏代码
  1. public void parsePropertyElement(Element ele, BeanDefinition bd) {  
  2.     String propertyName = ele.getAttribute(NAME_ATTRIBUTE);  
  3.     if (!StringUtils.hasLength(propertyName)) {  
  4.         error("Tag 'property' must have a 'name' attribute", ele);  
  5.         return;  
  6.     }  
  7.     this.parseState.push(new PropertyEntry(propertyName));  
  8.     try {  
  9.         if (bd.getPropertyValues().contains(propertyName)) {  
  10.             error("Multiple 'property' definitions for property '" + propertyName + "'", ele);  
  11.             return;  
  12.         }  
  13.         Object val = parsePropertyValue(ele, bd, propertyName);  
  14.         PropertyValue pv = new PropertyValue(propertyName, val);  
  15.         parseMetaElements(ele, pv);  
  16.         pv.setSource(extractSource(ele));  
  17.         bd.getPropertyValues().addPropertyValue(pv);  
  18.     }  
  19.     finally {  
  20.         this.parseState.pop();  
  21.     }  
  22. }  

 
解析的主要过程,首先判断是否重复,如果重复抛出异常,然后对property节点内部进行解析,最后加入到bean节点信息中,我们继续解析property节点内部,parsePropertyValue(ele, bd, propertyName)方法

Java代码  收藏代码
  1. public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {  
  2.     String elementName = (propertyName != null) ?  
  3.                     "<property> element for property '" + propertyName + "'" :  
  4.                     "<constructor-arg> element";  
  5.   
  6.     // Should only have one child element: ref, value, list, etc.  
  7.     NodeList nl = ele.getChildNodes();  
  8.     Element subElement = null;  
  9.     for (int i = 0; i < nl.getLength(); i++) {  
  10.         Node node = nl.item(i);  
  11.         if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&  
  12.                 !nodeNameEquals(node, META_ELEMENT)) {  
  13.             // Child element is what we're looking for.  
  14.             if (subElement != null) {  
  15.                 error(elementName + " must not contain more than one sub-element", ele);  
  16.             }  
  17.             else {  
  18.                 subElement = (Element) node;  
  19.             }  
  20.         }  
  21.     }  
  22.   
  23.     boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);  
  24.     boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);  
  25.     if ((hasRefAttribute && hasValueAttribute) ||  
  26.             ((hasRefAttribute || hasValueAttribute) && subElement != null)) {  
  27.         error(elementName +  
  28.                 " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);  
  29.     }  
  30.   
  31.     if (hasRefAttribute) {  
  32.         String refName = ele.getAttribute(REF_ATTRIBUTE);  
  33.         if (!StringUtils.hasText(refName)) {  
  34.             error(elementName + " contains empty 'ref' attribute", ele);  
  35.         }  
  36.         RuntimeBeanReference ref = new RuntimeBeanReference(refName);  
  37.         ref.setSource(extractSource(ele));  
  38.         return ref;  
  39.     }  
  40.     else if (hasValueAttribute) {  
  41.         TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));  
  42.         valueHolder.setSource(extractSource(ele));  
  43.         return valueHolder;  
  44.     }  
  45.     else if (subElement != null) {  
  46.         return parsePropertySubElement(subElement, bd);  
  47.     }  
  48.     else {  
  49.         // Neither child element nor "ref" or "value" attribute found.  
  50.         error(elementName + " must specify a ref or value", ele);  
  51.         return null;  
  52.     }  
  53. }  

 
property节点主要是value和ref属性的配置,所以此方法首先是配置以上两个属性,然后是分析property节点的子元素,parsePropertySubElement(subElement, bd)方法,重载到

Java代码  收藏代码
  1. public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {  
  2.     if (!isDefaultNamespace(getNamespaceURI(ele))) {  
  3.         return parseNestedCustomElement(ele, bd);  
  4.     }  
  5.     else if (nodeNameEquals(ele, BEAN_ELEMENT)) {  
  6.         BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);  
  7.         if (nestedBd != null) {  
  8.             nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);  
  9.         }  
  10.         return nestedBd;  
  11.     }  
  12.     else if (nodeNameEquals(ele, REF_ELEMENT)) {  
  13.         // A generic reference to any name of any bean.  
  14.         String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);  
  15.         boolean toParent = false;  
  16.         if (!StringUtils.hasLength(refName)) {  
  17.             // A reference to the id of another bean in the same XML file.  
  18.             refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);  
  19.             if (!StringUtils.hasLength(refName)) {  
  20.                 // A reference to the id of another bean in a parent context.  
  21.                 refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);  
  22.                 toParent = true;  
  23.                 if (!StringUtils.hasLength(refName)) {  
  24.                     error("'bean', 'local' or 'parent' is required for <ref> element", ele);  
  25.                     return null;  
  26.                 }  
  27.             }  
  28.         }  
  29.         if (!StringUtils.hasText(refName)) {  
  30.             error("<ref> element contains empty target attribute", ele);  
  31.             return null;  
  32.         }  
  33.         RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);  
  34.         ref.setSource(extractSource(ele));  
  35.         return ref;  
  36.     }  
  37.     else if (nodeNameEquals(ele, IDREF_ELEMENT)) {  
  38.         return parseIdRefElement(ele);  
  39.     }  
  40.     else if (nodeNameEquals(ele, VALUE_ELEMENT)) {  
  41.         return parseValueElement(ele, defaultValueType);  
  42.     }  
  43.     else if (nodeNameEquals(ele, NULL_ELEMENT)) {  
  44.         // It's a distinguished null value. Let's wrap it in a TypedStringValue  
  45.         // object in order to preserve the source location.  
  46.         TypedStringValue nullHolder = new TypedStringValue(null);  
  47.         nullHolder.setSource(extractSource(ele));  
  48.         return nullHolder;  
  49.     }  
  50.     else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {  
  51.         return parseArrayElement(ele, bd);  
  52.     }  
  53.     else if (nodeNameEquals(ele, LIST_ELEMENT)) {  
  54.         return parseListElement(ele, bd);  
  55.     }  
  56.     else if (nodeNameEquals(ele, SET_ELEMENT)) {  
  57.         return parseSetElement(ele, bd);  
  58.     }  
  59.     else if (nodeNameEquals(ele, MAP_ELEMENT)) {  
  60.         return parseMapElement(ele, bd);  
  61.     }  
  62.     else if (nodeNameEquals(ele, PROPS_ELEMENT)) {  
  63.         return parsePropsElement(ele);  
  64.     }  
  65.     else {  
  66.         error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);  
  67.         return null;  
  68.     }  
  69. }  

 

 这个方法内部也是一目了然,首先是配置property节点的内嵌bean,然后配置ref引用,然后是idref引用,接下来是内嵌的value元素,还有null元素,最后是一系列的复杂数据类型,array,list,set,map以及props。

展开阅读全文

没有更多推荐了,返回首页