【02】spring源码解析 XML配置文件的读取以及转化为Bean对象的过程解析

 本文档针对spring4.2.x版本 java

Spring IOC容器初始化的过程,分为定位,载入解析以及注册,接下来本文主要分析的是spring如何去解析BeanBeanDefinition对象的,这个只对xml声明的Bean进行分析,对于经过spring注解扫描的方式之后再作分析 node

spring定位到springxml文件之后,将xml读做为文件流的形式,做为InputSourceResource对象传递给文档解析器进行解析,文档解析的开始是XmlBeanDefinitionReaderdoLoadBeanDefinitions方法。 spring

inputSource是SAX的InputSource,
resource对象是对xml文件描述的一个对象
 
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
         throws BeanDefinitionStoreException {
      try {
         /**
 
    传递inputSource对象进入doLoadDocument方法,此方法中设置了xml的版本,xml解析的模式等相关信息,而后input    Source文件流读做为Document对象返回,把Document对象传递给registerBeanDefinitions方法,这个方法才是真正的把Document对象解析为BeanDefinition对象的具体实现
         **/
         Document doc = doLoadDocument(inputSource, resource);
         return registerBeanDefinitions(doc, resource);
      } catch (BeanDefinitionStoreException ex) {
         throw ex;
     } catch
        …
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
      BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
      int countBefore = getRegistry().getBeanDefinitionCount();
      documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
      return getRegistry().getBeanDefinitionCount() - countBefore;
  }


先实例化出来一个BeanDefinitionDocumentReader,这个对象是Java经过本身封装工具类BeanUtils.instantiateClass 经过反射的方式实例化出来的,而后记录一下在注册以前BeanDefinition中对象的个数,接着开始去解析documentspring自己设计很是灵活,BeanDefinitionDocumentReader registerBeanDefinitions方法是一个抽象方法,spring自身实现一个默认的BeanDefinitionDocumentReader的一个注册器,DefaultBeanDefinitionDocumentReader,在这个子类中去实现具体的解析工做。 ide

@Override
   public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
      this.readerContext = readerContext;
      logger.debug("Loading bean definitions");
      Element root = doc.getDocumentElement();
      doRegisterBeanDefinitions(root);
  }
 
protected void doRegisterBeanDefinitions(Element root) {
 
BeanDefinitionParserDelegate对象描述了spring中bean节点中定义的所全部属性和子节点
 
//全部的内嵌<beans>节点,都会被递归这个方法中经过递归的方式找到
//<beans>节点的默认属性都会在此方法中设置,
//保存当前对象(或者父对象)BeanDefinitionParserDelegate轨迹,可能为空,可是会建立
//子类的属性描述(BeanDefinitionParserDelegate)会持有父类的一个引用
      
      BeanDefinitionParserDelegate parent = this.delegate;
      this.delegate = createDelegate(getReaderContext(), root, parent);
      
      if (this.delegate.isDefaultNamespace(root)) {
         String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
         if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                   profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
         }
      }
      //在xml解析的预处理,客户端能够本身定义一些本身的节点属性,用以特殊的做用,或者加强,此方法spring默认实现为空
      preProcessXml(root);
//把Document对象解析为BeanDefinition对象
      parseBeanDefinitions(root, this.delegate);
//在xml解析为BeanDefinition以后作一些后置处理,客户端能够在解析完xml以后,作一些本身的业务逻辑,目前spring的默认实现为空
      postProcessXml(root);
      this.delegate = parent;
   }

接下来分析parseBeanDefinition方法 工具

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//校验是否是spring的默认命名空间,默认命名空间为http://www.springframework.org/schema/beans,若是是默认命名空间,则按照默认命名空间来解析,不然则按照客户自定义程序来解析
      if (delegate.isDefaultNamespace(root)) {
         NodeList nl = root.getChildNodes();
         for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
 
//循环xml节点,看delegate对象是不是默认命名空间,若是是则按照默认命名空间来解析节点,不然则按照客户自定义来解析
                if (delegate.isDefaultNamespace(ele)) {
                   parseDefaultElement(ele, delegate);
                }
                else {
                   delegate.parseCustomElement(ele);
                }
            }
         }
      }
      else {
         delegate.parseCustomElement(root);
      }
  }

下面对parseDefaultElement 方法进行分析 post

此方法会查询文档中的import 标记,alias标记,以及beanbeans标记,根据这些标记进行分别匹配作相应的解析工做 this

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);
      }
  }

一、     Import节点的解析 spa

首先要求import节点中必须有resource属性,而后根据resource的值判断是相对路径仍是绝对路径。 debug

而且对于resource中的占位符进行解析,例如"${user.dir}" 解析为真正的路径 设计

而后调用XmlBeanDefinitionReaderloadBeanDefinitions方法,把import节点映射为BeanDefinition对象,初始化了一个4个元素大小的LinkHashSet对象,准备好相关容器和资源,而后调用本类中的doLoadBeanDefinitions方法,作真正的映射工做 ,这是一个递归的过程,它须要把全部的import节点中的xml文件递归的找出来,而后作的均是bean节点的解析

二、     Alias节点解析

三、     Bean节点解析

使用list存储bean信息

/** Map of bean definition objects, keyed by bean name */

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

/** List of bean definition names, in registration order */

private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);

 /** List of names of manually registered singletons, in registration order */

private volatile Set<String> manualSingletonNames = new LinkedHashSet<String>(16);

把以上信息进行存储完之后逐个转化为BeanDefinition的属性

四、     Beans节点解析

 最终转化为bean节点的解析

相关文章
相关标签/搜索