Spring源码解析和配置文件加载

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对象.

Java代码  

  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这个类.

Java代码  

  1. public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader

接着

Java代码  

  1. public abstract class AbstractBeanDefinitionReader
  2. implements BeanDefinitionReader

再继续

Java代码  

  1. public interface BeanDefinitionReader

在BeanDefinitionReader中定义有许多loadBeanDefinitions方法

Java代码  

  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方法

Java代码  

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

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

Java代码  

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

跟踪到DefaultDocumentLoader

Java代码  

  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方法

Java代码  

  1. return registerBeanDefinitions(doc, resource);

来看看registerBeanDefinitions的实现

Java代码  

  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, 来看看它的家族

Java代码  

  1. public class DefaultBeanDefinitionDocumentReader

Java代码  

  1. public interface BeanDefinitionDocumentReader

BeanDefinitionDocumentReader只有一个registerBeanDefinitions方法

Java代码  

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

该方法需要两个参数, 一个是Document模型,这个应该是我们读取配置文件获取到的, 另一个是XmlReaderContext对象, 我们在上面方法中看到是通过createReaderContext(resource)得到的, 那就看看具体如何得到

Java代码  

  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方法实现

Java代码  

  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方法, 来看看具体是如何解析的

Java代码  

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
  2. {
  3. if(delegate.isDefaultNamespace(root.getNamespaceURI()))
  4. {
  5. NodeList nl = root.getChildNodes();
  6. for(int i = 0; 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方法

Java代码  

  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配置文件中常有以下几种配置

Java代码  

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

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

Java代码  

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

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

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

Java代码  

  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方法

Java代码  

  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方法

Java代码  

  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, 来看看如何实现的

Java代码  

  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. for(int i = 0; i < aliases.length; i++)
  12. beanFactory.registerAlias(beanName, aliases[i]);
  13. }
  14. }
  15. }

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

Java代码  

  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抽象类所定义的

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2025-01-02 03:02:58

Spring源码解析和配置文件加载的相关文章

Spring源码解析-applicationContext.xml加载和bean的注册

applicationContext文件加载和bean注册流程? Spring对于从事Java开发的boy来说,再熟悉不过了,对于我们这个牛逼的框架的介绍就不在这里复述了,Spring这个大杂烩,怎么去使用怎么去配置,各种百度谷歌都能查到很多大牛教程,但是,当我们按着教程一步步的把spring的开发框架搭建起来的时候,有没有一种想搞明白spring的冲动,万事开头难,就要从开头开始,而我认为spring开头就是如何加载配置文件,并初始化配置文件里面的bean当然也包括了我们用注解Service.

【Spring源码分析】Bean加载流程概览

代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已,Spring的加载过程相对是不太透明的,不太好去找加载的代码入口. 下面有很简单的一段代码可以作为Spring代码加载的入口: 1 ApplicationContext ac = new Clas

spring源码阅读(二) Bean加载之自定义标签加载

紧接着上一篇关于spring默认标签加载,这一篇来看下自定义标签的加载 继续从 DefaultBeanDefinitionDocumentReader来看 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for(

spring源码阅读(一) Bean加载之默认标签加载

接着上文的内容,我们经历了xml资源文件的校验/解析/终于要进入到Bean的加载中了. 上文进行到: protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for(int i = 0; i < nl.getLength

【Spring源码分析】配置文件读取流程

前言 Spring配置文件读取流程本来是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spring配置文件读取流程还是研究得不够,因此将Spring配置文件读取流程部分从之前的文章拆出来单独成为一文. 为了看一下Spring配置文件加载流程,先定义一个bean.xml: 1 <?xml version="1.0" encoding="UTF-8"?>

软件开发工程师(JAVA)中级考试大纲--spring源码解析

spring源码解析(1)----IOC 一.IOC容器 在Spring中,IOC容器的重要地位我们就不多说了,对于Spring的使用者而言,IOC容器实际上是什么呢?我们可以说BeanFactory就是我们看到的IoC容器,当然了Spring为我们准备了许多种IoC容器来使用,这样可以方便我们从不同的层面,不同的资源位置,不同的形式的定义信息来建立我们需要的IoC容器. 在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 

Spring 源码解析之DispatcherServlet源码解析(五)

Spring 源码解析之DispatcherServlet源码解析(五) 前言 本文需要有前四篇文章的基础,才能够清晰易懂,有兴趣可以先看看详细的流程,这篇文章可以说是第一篇文章,也可以说是前四篇文章的的汇总,Spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一个Servlet来进行转发的,同时解释了为什么需要在web.xml进行如下配置,因为Spr

SPRING源码解析-SPRING 核心-IOC

IoC 和 AOP是Spring的核心, 是Spring系统中其他组件模块和应用开发的基础.透过这两个模块的设计和实现可以了解Spring倡导的对企业应用开发所应秉承的思路: 易用性. POJO开发企业应用, 直接依赖于Java语言,而不是容器和框架. 提升程序的可测试性,提高软件质量. 提供一致性编程模型,面向接口的编程 降低应用的负载和框架的侵入性.IoC和AOP实现. 不作为现有解决方案的替代,而是集成现有. IoC和AOP这两个核心组件,特别是IoC容器,使用户在使用Spring完成PO

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge