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

原创
2016/03/13 10:21
阅读数 1.2K

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

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

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

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,在这个子类中去实现具体的解析工作。

@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 方法进行分析

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

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

1、     Import节点的解析

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

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

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

2、     Alias节点解析

3、     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的属性

4、     Beans节点解析

 最终转化为bean节点的解析


展开阅读全文
打赏
1
7 收藏
分享
加载中
更多评论
打赏
0 评论
7 收藏
1
分享
返回顶部
顶部