文档章节

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

二两豆腐
 二两豆腐
发布于 2016/03/13 10:21
字数 1311
阅读 496
收藏 7

 本文档针对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节点的解析


© 著作权归作者所有

二两豆腐
粉丝 21
博文 95
码字总数 79432
作品 0
朝阳
高级程序员
私信 提问
【Spring】BeanFactory解析bean详解

在该文中来讲讲Spring框架中BeanFactory解析bean的过程,该文之前在小编原文中有发表过,要看原文的可以直接点击原文查看,先来看一个在Spring中一个基本的bean定义与使用。 Spring配置文件r...

weknow
2017/04/05
0
0
深入理解Spring源码(一)-IOC容器的定位,载入,注册

前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰。下面开始正题 1.Spring...

Meet相识_bfa5
2018/05/01
0
0
【Spring】BeanFactory解析bean详解

本文是Spring源码分析中的一篇,来讲讲Spring框架中BeanFactory解析bean的过程,先来看一个在Spring中一个基本的bean定义与使用。(也可以点击公号查看) Spring配置文件root.xml定义如下: ...

weknow
2018/09/09
0
0
spring源码-bean之初始化-1

  一、spring的IOC控制反转:控制反转——Spring通过一种称作控制反转(IOC)的技术促进了松耦合。当应用了IOC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建...

小不点丶
2018/08/09
0
0
Spring Bean注册解析(一)

Spring是通过IoC容器对Bean进行管理的,而Bean的初始化主要分为两个过程:Bean的注册和Bean实例化。Bean的注册主要是指Spring通过读取配置文件获取各个bean的声明信息,并且对这些信息进行注...

张旭峰
2018/05/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

G1 垃圾收集器介绍-转

https://www.cnblogs.com/ASPNET2008/p/6496481.html

Java搬砖工程师
28分钟前
1
0
超高性能 key-value 数据库 Redis-基础数据结构

Redis的魅力 缓存大致可以分为两类:1.一种是应用内缓存,比如Map(简单的数据结构),以及EH Cache(Java第三方库);2.另一种 就是缓存组件,比如Memached,Redis;Redis(remote dictiona...

须臾之余
39分钟前
3
0
Mysql表分区的选择与实践小结

在一些系统中有时某张表会出现百万或者千万的数据量,尽管其中使用了索引,查询速度也不一定会很快。这时候可能就需要通过分库,分表,分区来解决这些性能瓶颈。 一. 选择合适的解决方法 1....

小谜弟
45分钟前
3
0
为 git 添加多个公秘钥

如果想为主机配置多个git设置,设置多个git公、秘钥,只需在生成密钥时指定密钥保持的文件即可,保证保存密钥的文件不同即可。 示例: ssh-keygen -t rsa -C "YOUR_EMAIL@YOUREMAIL.COM" -f...

niithub
45分钟前
2
0
walle-web 2.0安装流水

一、环境安装 VMware Workstation,centos7.6 64位,lnmp1.5 二、安装lnmp1.5 wget http://soft.vpser.net/lnmp/lnmp1.5.tar.gz -cO lnmp1.5.tar.gz && tar zxf lnmp1.5.tar.gz && cd lnmp1......

我心中有猛狗
47分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部