Spring类的继承结构图:



Spring运用了大量的模板方法模式和策略模式,所以各位看源代码的时候,务必留意,每个继承的层次都有不同的作用。然后将同样的地方抽取出来,依赖抽象将不同的处理依照不同的策略去处理。

步骤A. 读取 Resource 文件形成 Document 模型 

类图: XmlBeanFactory -> XmlBeanDefinitionReader 

    

    Spring 使用 XmlBeanDefinitionReader 来读取并解析 xml 文件,XmlBeanDefinitionReader 是 BeanDefinitionReader 接口的实现。 

    BeanDefinitionReader 定义了 Spring 读取 Bean 定义的一个接口,这个接口中有一些 loadBeanDefinitions 方法, 用于读取 Bean 配置。

BeanDefinitionReader 接口有两个详细的实现。当中之中的一个就是从 Xml 文件里读取配置的 XmlBeanDefinitionReader,还有一个则是从 Java Properties 文件里读取配置的PropertiesBeanDefinitionReader。

(注:开发者也能够提供自己的 BeanDefinitionReader 实现。依据自己的须要来读取 spring bean 定义的配置。





步骤B. 解析 Document 得到 Bean 配置 



    类图: XmlBeanDefinitionReader-> BeanDefinitionDocumentReader 



    BeanDefinitionDocumentReader 接口中仅仅定义了一个方法 registerBeanDefinitions. 有一个默认实现 DefaultBeanDefinitionDocumentReader. 

    DefaultBeanDefinitionDocumentReader 主要完毕两件事情,解析 <bean> 元素。为扩展 spring 的元素寻找合适的解析器,并把对应的元素交给解析器解析。 



过程: 

    在 XmlBeanFactory 中创建了 XmlBeanDefinitionReader 的实例,并在 XmlBeanFactory 的构造方法中调用了 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法,由 loadBeanDefinitions
方法负责载入 bean 配置并把 bean 配置注冊到 XmlBeanFactory 中。

在 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法中, 调用 DefaultDocumentLoader 的 loadDocument 读取配置文件为 Document, 然后调用 BeanDefinitionDocumentReader
的 registerBeanDefinitions 方法 来解析 Bean. 



源代码解析: 



在XmlBeanFactory初始化时, 须要指定Resource对象.

  1. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
  2. throws BeansException
  3. {
  4. super(parentBeanFactory);
  5. reader = new XmlBeanDefinitionReader(this);
  6. reader.loadBeanDefinitions(resource);
  7. }

1. 先来分析下XmlBeanDefinitionReader这个类.

  1. public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader

接着

  1. public abstract class AbstractBeanDefinitionReader
  2. implements BeanDefinitionReader

再继续

  1. public interface BeanDefinitionReader

在BeanDefinitionReader中定义有很多loadBeanDefinitions方法

  1. public abstract int loadBeanDefinitions(Resource resource)
  2. throws BeanDefinitionStoreException;
  3. public abstract int loadBeanDefinitions(Resource aresource[])
  4. throws BeanDefinitionStoreException;
  5. public abstract int loadBeanDefinitions(String s)
  6. throws BeanDefinitionStoreException;
  7. public abstract int loadBeanDefinitions(String as[])
  8. throws BeanDefinitionStoreException;

来回头看XmlBeanDefinitionReader对loadBeanDefinitions方法的实现 

在loadBeanDefinitions方法中调用了doLoadBeanDefinitions方法, 跟踪doLoadBeanDefinitions方法

  1. Document doc = documentLoader.loadDocument(inputSource, getEntityResolver(), errorHandler, validationMode, isNamespaceAware());

通过一个叫documentLoader的东西的loadDocument方法来载入配置文件形成DOM, 来看看documentLoader

  1. private DocumentLoader documentLoader
  2. ...
  3. documentLoader = new DefaultDocumentLoader();

跟踪到DefaultDocumentLoader

  1. public class DefaultDocumentLoader
  2. implements DocumentLoader
  3. ...
  4. public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
  5. throws Exception
  6. {
  7. DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
  8. if(logger.isDebugEnabled())
  9. logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
  10. DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
  11. return builder.parse(inputSource);
  12. }
  13. ...

哦哦, 我们知道了, 是通过sax解析得到Dom的, 至于怎么解析, 不属于Spring范畴, 不做研究. 



在这一步, 已完毕了从配置文件读取到Domcument. 接着要開始解析Dom了 



再继续, 解析成Dom后接着调用了registerBeanDefinitions方法

  1. return registerBeanDefinitions(doc, resource);

来看看registerBeanDefinitions的实现

  1. ...
  2. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  3. int countBefore = getRegistry().getBeanDefinitionCount();
  4. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  5. return getRegistry().getBeanDefinitionCount() - countBefore;
  6. ...

在这里, 有一个BeanDefinitionDocumentReader接口, 实际上Spring对它有一个默认的实现类叫DefaultBeanDefinitionDocumentReader, 来看看它的家族

  1. public class DefaultBeanDefinitionDocumentReader
  1. public interface BeanDefinitionDocumentReader

BeanDefinitionDocumentReader仅仅有一个registerBeanDefinitions方法

  1. public abstract void registerBeanDefinitions(Document document, XmlReaderContext xmlreadercontext)
  2. throws BeanDefinitionStoreException;

该方法须要两个參数, 一个是Document模型,这个应该是我们读取配置文件获取到的, 还有一个是XmlReaderContext对象, 我们在上面方法中看到是通过createReaderContext(resource)得到的, 那就看看详细怎样得到

  1. protected XmlReaderContext createReaderContext(Resource resource)
  2. {
  3. if(namespaceHandlerResolver == null)
  4. namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
  5. return new XmlReaderContext(resource, problemReporter, eventListener, sourceExtractor, this, namespaceHandlerResolver);
  6. }

能过构造函数new出来的, 且有一个重要參数resource 

再继续来看DefaultBeanDefinitionDocumentReader对BeanDefinitionDocumentReader的registerBeanDefinitions方法实现

  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
  2. {
  3. ...
  4. BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
  5. ...
  6. parseBeanDefinitions(root, delegate);
  7. ...
  8. }

嘿嘿, 開始解析Dom了哦, 当中主要是parseBeanDefinitions方法, 来看看详细是怎样解析的

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

看到了吧, 循环解析Domcument节点 

parseDefaultElement方法和delegate的parseCustomElement方法 

先来看parseDefaultElement方法

  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
  2. {
  3. if(DomUtils.nodeNameEquals(ele, "import"))
  4. importBeanDefinitionResource(ele);
  5. else
  6. if(DomUtils.nodeNameEquals(ele, "alias"))
  7. processAliasRegistration(ele);
  8. else
  9. if(DomUtils.nodeNameEquals(ele, "bean"))
  10. processBeanDefinition(ele, delegate);
  11. }

看到这就非常清楚了, 就是依据节点的名称作不同解析, 如我们Spring配置文件里常有下面几种配置

  1. <import resource="classpath:xxx" />
  2. <bean id="xxx" class="xxx.xxx.xxx" />
  3. <alias name="xxxx" alias="yyyyy"/>

对<import>节点, 调用importBeanDefinitionResource方法解析, 此方法中, 又回到第一步读取配置文件并解析. 如此递归循环.

  1. ...
  2. Resource relativeResource = getReaderContext().getResource().createRelative(location);
  3. int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
  4. ...

对<alias>节点, 调用processAliasRegistration进行别名解析 

我们主要看对<bean>节点调用processBeanDefinition进行解析

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

嘿嘿, 又用到delegate对象了, 且调用它的BeanDefinitionHolder方法, 返回一个BeanDefinitionHolder, 进去看它的parseBeanDefinitionElement方法

  1. public class BeanDefinitionParserDelegate
  2. {
  3. private final Set usedNames = new HashSet();
  4. ...
  5. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)
  6. {
  7. ... 解析id, name等属性, 并验证name是否唯一, 并将name保存在usedNames中
  8. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  9. ...
  10. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  11. }
  12. ...
  13. }

能够看到, 在BeanDefinitionHolder中保存了BeanDefinition的定义 

OK, 重头戏開始, 最经典的部分出现了, 请看parseBeanDefinitionElement方法

  1. public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)
  2. {
  3. ...
  4. 代码太长, 请參考详细代码
  5. AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(parent, className, readerContext.getBeanClassLoader());
  6. ...
  7. return abstractbeandefinition;
  8. ...
  9. }

在这种方法中, 解析了bean的全部属性, 有最经常使用的class, scope, lazy-init等等. 并返回一个AbstractBeanDefinition实例. 至于详细怎么解析, 就仅仅能进一步跟踪了, 只是既然到了这一步, 已经明确了它的基本原理, 详细实现就不作介绍 



这一步将节点解析成BeanDefinitionHolder对象, 再看看怎样注冊, 回到DefaultBeanDefinitionDocumentReader的processBeanDefinition方法 

看到对解析到的bdHolder对象又做了decorateBeanDefinitionIfRequired操作, 来看看实现 

... 暂留空 



接着调用了BeanDefinitionReaderUtils的registerBeanDefinition方法注冊bdHolder, 来看看怎样实现的

  1. public class BeanDefinitionReaderUtils
  2. {
  3. public static void registerBeanDefinition(BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory)
  4. throws BeanDefinitionStoreException
  5. {
  6. String beanName = bdHolder.getBeanName();
  7. beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition());
  8. String aliases[] = bdHolder.getAliases();
  9. if(aliases != null)
  10. {
  11. ; i < aliases.length; i++)
  12. beanFactory.registerAlias(beanName, aliases[i]);
  13. }
  14. }
  15. }

看吧, 又调用了BeanDefinitionRegistry的registerBeanDefinition方法, 跟踪之 (这个要看DefaultListableBeanFactory的实现)

  1. public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
  2. implements ConfigurableListableBeanFactory, BeanDefinitionRegistry
  3. {
  4. private final Map beanDefinitionMap;
  5. private final List beanDefinitionNames;
  6. ...
  7. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  8. throws BeanDefinitionStoreException
  9. {
  10. ...
  11. Object oldBeanDefinition = beanDefinitionMap.get(beanName);
  12. ...
  13. beanDefinitionMap.put(beanName, beanDefinition);
  14. ...
  15. }
  16. }

这里, 看到了一个最最重要的对象就是beanDefinitionMap, 这个map存放了全部的bean对象, 和我们通常讲的容器概念最为接近, getBean时实际是也是从这里辚取对象, 同样的另一个beanDefinitionNames, 但这个仅仅保存bean的名称 

完毕上面之后, 另一步操作beanFactory.registerAlias(beanName, aliases[i]); 

这个实现实际是上AbstractBeanFactory抽象类所定义的

Spring源代码解析和配置文件载入的更多相关文章

  1. Spring源代码解析

    Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.itey ...

  2. Spring源代码解析(收藏)

    Spring源代码解析(收藏)   Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的 ...

  3. Spring源代码解析 ---- 循环依赖

    一.循环引用: 1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用Circularit ...

  4. Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载

    转: Spring中property-placeholder的使用与解析 Spring中property-placeholder的使用与解析 我们在基于spring开发应用的时候,一般都会将数据库的配 ...

  5. android7.x Launcher3源代码解析(3)---workspace和allapps载入流程

    Launcher系列目录: 一.android7.x Launcher3源代码解析(1)-启动流程 二.android7.x Launcher3源代码解析(2)-框架结构 三.android7.x L ...

  6. MyBatis官方教程及源代码解析——mapper映射文件

    缓存 1.官方文档 MyBatis 包括一个非常强大的查询缓存特性,它能够非常方便地配置和定制. MyBatis 3 中的缓存实现的非常多改进都已经实现了,使得它更加强大并且易于配置. 默认情况下是没 ...

  7. Spring使用外部的配置文件

    在使用Spring做web项目的时候,通常会使用到数据库的连接信息 jdbcUrl driverClass username password 那么应该如何使用这些属性呢? 如在Spring中使用数据 ...

  8. volley源代码解析(七)--终于目的之Response&lt;T&gt;

    在上篇文章中,我们终于通过网络,获取到了HttpResponse对象 HttpResponse是android包里面的一个类.然后为了更高的扩展性,我们在BasicNetwork类里面看到.Volle ...

  9. Android xUtils3源代码解析之网络模块

    本文已授权微信公众号<非著名程序猿>原创首发,转载请务必注明出处. xUtils3源代码解析系列 一. Android xUtils3源代码解析之网络模块 二. Android xUtil ...

随机推荐

  1. 阿里云虚拟主机针对恶意频繁攻击式访问造成CPU爆满的解决方法

    最近网站CPU经常爆满,到阿里云提交了工单,工程师给我的处理意见:   您好,虚拟主机CPU占用比较高通常这种情况有两种可能:   一是网站应用程序代码逻辑较复杂,或业务架构效率比较低,在请求了某个网 ...

  2. html中设置浏览器解码方式

    通过添加一行标签: <meta http-equiv="Content-Type" content="text/html; charset=utf-8"& ...

  3. 【译】x86程序员手册03 - 2.1内存组织和分段

    2.1 Memory Organization and Segmentation 内存组织和分段 The physical memory of an 80386 system is organized ...

  4. Centos6.6 安装rsync服务端

    一.介绍 在工作中经常遇到代码分发,或者是资料备份,都会用到rsync,配置不算复杂,仅做下记录,安装环境如下: 1) Centos6.6 2) rsync-3.0.6-12.el6.x86_64 3 ...

  5. centos安装指定mysql

    mysql下载地址:http://repo.mysql.com/ nginx下载地址 我下载是这个 http://nginx.org/packages/centos/7/noarch/RPMS/ngi ...

  6. CAD得到自定义实体拖放夹点(com接口VB语言)

    主要用到函数说明: MxDrawXCustomEvent::MxDrawXCustomEntity::getGripPoints 自定义实体事件,得到拖放夹点,详细说明如下: 参数 说明 LONGLO ...

  7. adjtimex和时钟的几个概念tick,freq,ppm,jiffies

    adjtimex使用 今天遇到一个ntp的同步问题.服务器上配置好了ntpd,在启动前也手动进行过同步,但是过段时间ntpq查询发现服务器即便能选出同步服务器,但是系统的时间偏差越来越大. 服务器上实 ...

  8. React 服务器渲染原理解析与实践

    第1章 服务器端渲染基础本章主要讲解客户端与服务器端渲染的概念,分析客户端渲染和服务器端渲染的利弊,带大家对服务器端渲染有一个粗浅认识. 1-1 课程导学1-2 什么是服务器端渲染1-3 什么是客户端 ...

  9. SDOI2018退役记

    在NOIp2017中,我意识到自己啥也不会.如今SDOI2018快来了,自己还是啥也不会.高一两次考试注定以打两次酱油告终.还是记录一下,到NOIp之后如果还没有退役的话,那这个博客可能还会继续更新吧 ...

  10. Luogu P1187 3D模型

    题目描述 一座城市建立在规则的n×m网格上,并且网格均由1×1正方形构成.在每个网格上都可以有一个建筑,建筑由若干个1×1×1的立方体搭建而成(也就是所有建筑的底部都在同一平面上的).几个典型的城市模 ...