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对象.
- public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
- throws BeansException
- {
- super(parentBeanFactory);
- reader = new XmlBeanDefinitionReader(this);
- reader.loadBeanDefinitions(resource);
- }
1. 先来分析下XmlBeanDefinitionReader这个类.
- public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader
接着
- public abstract class AbstractBeanDefinitionReader
- implements BeanDefinitionReader
再继续
- public interface BeanDefinitionReader
在BeanDefinitionReader中定义有很多loadBeanDefinitions方法
- public abstract int loadBeanDefinitions(Resource resource)
- throws BeanDefinitionStoreException;
- public abstract int loadBeanDefinitions(Resource aresource[])
- throws BeanDefinitionStoreException;
- public abstract int loadBeanDefinitions(String s)
- throws BeanDefinitionStoreException;
- public abstract int loadBeanDefinitions(String as[])
- throws BeanDefinitionStoreException;
来回头看XmlBeanDefinitionReader对loadBeanDefinitions方法的实现
在loadBeanDefinitions方法中调用了doLoadBeanDefinitions方法, 跟踪doLoadBeanDefinitions方法
- Document doc = documentLoader.loadDocument(inputSource, getEntityResolver(), errorHandler, validationMode, isNamespaceAware());
通过一个叫documentLoader的东西的loadDocument方法来载入配置文件形成DOM, 来看看documentLoader
- private DocumentLoader documentLoader
- ...
- documentLoader = new DefaultDocumentLoader();
跟踪到DefaultDocumentLoader
- public class DefaultDocumentLoader
- implements DocumentLoader
- ...
- public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
- throws Exception
- {
- DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
- if(logger.isDebugEnabled())
- logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
- DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
- return builder.parse(inputSource);
- }
- ...
哦哦, 我们知道了, 是通过sax解析得到Dom的, 至于怎么解析, 不属于Spring范畴, 不做研究.
在这一步, 已完毕了从配置文件读取到Domcument. 接着要開始解析Dom了
再继续, 解析成Dom后接着调用了registerBeanDefinitions方法
- return registerBeanDefinitions(doc, resource);
来看看registerBeanDefinitions的实现
- ...
- BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
- int countBefore = getRegistry().getBeanDefinitionCount();
- documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
- return getRegistry().getBeanDefinitionCount() - countBefore;
- ...
在这里, 有一个BeanDefinitionDocumentReader接口, 实际上Spring对它有一个默认的实现类叫DefaultBeanDefinitionDocumentReader, 来看看它的家族
- public class DefaultBeanDefinitionDocumentReader
- public interface BeanDefinitionDocumentReader
BeanDefinitionDocumentReader仅仅有一个registerBeanDefinitions方法
- public abstract void registerBeanDefinitions(Document document, XmlReaderContext xmlreadercontext)
- throws BeanDefinitionStoreException;
该方法须要两个參数, 一个是Document模型,这个应该是我们读取配置文件获取到的, 还有一个是XmlReaderContext对象, 我们在上面方法中看到是通过createReaderContext(resource)得到的, 那就看看详细怎样得到
- protected XmlReaderContext createReaderContext(Resource resource)
- {
- if(namespaceHandlerResolver == null)
- namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
- return new XmlReaderContext(resource, problemReporter, eventListener, sourceExtractor, this, namespaceHandlerResolver);
- }
能过构造函数new出来的, 且有一个重要參数resource
再继续来看DefaultBeanDefinitionDocumentReader对BeanDefinitionDocumentReader的registerBeanDefinitions方法实现
- public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
- {
- ...
- BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
- ...
- parseBeanDefinitions(root, delegate);
- ...
- }
嘿嘿, 開始解析Dom了哦, 当中主要是parseBeanDefinitions方法, 来看看详细是怎样解析的
- protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
- {
- if(delegate.isDefaultNamespace(root.getNamespaceURI()))
- {
- NodeList nl = root.getChildNodes();
- ; i < nl.getLength(); i++)
- {
- org.w3c.dom.Node node = nl.item(i);
- if(node instanceof Element)
- {
- Element ele = (Element)node;
- String namespaceUri = ele.getNamespaceURI();
- if(delegate.isDefaultNamespace(namespaceUri))
- parseDefaultElement(ele, delegate);
- else
- delegate.parseCustomElement(ele);
- }
- }
- } else
- {
- delegate.parseCustomElement(root);
- }
- }
看到了吧, 循环解析Domcument节点
parseDefaultElement方法和delegate的parseCustomElement方法
先来看parseDefaultElement方法
- private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
- {
- if(DomUtils.nodeNameEquals(ele, "import"))
- importBeanDefinitionResource(ele);
- else
- if(DomUtils.nodeNameEquals(ele, "alias"))
- processAliasRegistration(ele);
- else
- if(DomUtils.nodeNameEquals(ele, "bean"))
- processBeanDefinition(ele, delegate);
- }
看到这就非常清楚了, 就是依据节点的名称作不同解析, 如我们Spring配置文件里常有下面几种配置
- <import resource="classpath:xxx" />
- <bean id="xxx" class="xxx.xxx.xxx" />
- <alias name="xxxx" alias="yyyyy"/>
对<import>节点, 调用importBeanDefinitionResource方法解析, 此方法中, 又回到第一步读取配置文件并解析. 如此递归循环.
- ...
- Resource relativeResource = getReaderContext().getResource().createRelative(location);
- int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
- ...
对<alias>节点, 调用processAliasRegistration进行别名解析
我们主要看对<bean>节点调用processBeanDefinition进行解析
- protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
- {
- BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
- if(bdHolder != null)
- {
- bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
- try
- {
- BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
- }
- catch(BeanDefinitionStoreException ex)
- {
- getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
- }
- getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
- }
- }
嘿嘿, 又用到delegate对象了, 且调用它的BeanDefinitionHolder方法, 返回一个BeanDefinitionHolder, 进去看它的parseBeanDefinitionElement方法
- public class BeanDefinitionParserDelegate
- {
- private final Set usedNames = new HashSet();
- ...
- public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)
- {
- ... 解析id, name等属性, 并验证name是否唯一, 并将name保存在usedNames中
- AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
- ...
- return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
- }
- ...
- }
能够看到, 在BeanDefinitionHolder中保存了BeanDefinition的定义
OK, 重头戏開始, 最经典的部分出现了, 请看parseBeanDefinitionElement方法
- public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)
- {
- ...
- 代码太长, 请參考详细代码
- AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(parent, className, readerContext.getBeanClassLoader());
- ...
- return abstractbeandefinition;
- ...
- }
在这种方法中, 解析了bean的全部属性, 有最经常使用的class, scope, lazy-init等等. 并返回一个AbstractBeanDefinition实例. 至于详细怎么解析, 就仅仅能进一步跟踪了, 只是既然到了这一步, 已经明确了它的基本原理, 详细实现就不作介绍
这一步将节点解析成BeanDefinitionHolder对象, 再看看怎样注冊, 回到DefaultBeanDefinitionDocumentReader的processBeanDefinition方法
看到对解析到的bdHolder对象又做了decorateBeanDefinitionIfRequired操作, 来看看实现
... 暂留空
接着调用了BeanDefinitionReaderUtils的registerBeanDefinition方法注冊bdHolder, 来看看怎样实现的
- public class BeanDefinitionReaderUtils
- {
- public static void registerBeanDefinition(BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory)
- throws BeanDefinitionStoreException
- {
- String beanName = bdHolder.getBeanName();
- beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition());
- String aliases[] = bdHolder.getAliases();
- if(aliases != null)
- {
- ; i < aliases.length; i++)
- beanFactory.registerAlias(beanName, aliases[i]);
- }
- }
- }
看吧, 又调用了BeanDefinitionRegistry的registerBeanDefinition方法, 跟踪之 (这个要看DefaultListableBeanFactory的实现)
- public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
- implements ConfigurableListableBeanFactory, BeanDefinitionRegistry
- {
- private final Map beanDefinitionMap;
- private final List beanDefinitionNames;
- ...
- public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
- throws BeanDefinitionStoreException
- {
- ...
- Object oldBeanDefinition = beanDefinitionMap.get(beanName);
- ...
- beanDefinitionMap.put(beanName, beanDefinition);
- ...
- }
- }
这里, 看到了一个最最重要的对象就是beanDefinitionMap, 这个map存放了全部的bean对象, 和我们通常讲的容器概念最为接近, getBean时实际是也是从这里辚取对象, 同样的另一个beanDefinitionNames, 但这个仅仅保存bean的名称
完毕上面之后, 另一步操作beanFactory.registerAlias(beanName, aliases[i]);
这个实现实际是上AbstractBeanFactory抽象类所定义的
Spring源代码解析和配置文件载入的更多相关文章
- Spring源代码解析
Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.itey ...
- Spring源代码解析(收藏)
Spring源代码解析(收藏) Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的 ...
- Spring源代码解析 ---- 循环依赖
一.循环引用: 1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用Circularit ...
- Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载
转: Spring中property-placeholder的使用与解析 Spring中property-placeholder的使用与解析 我们在基于spring开发应用的时候,一般都会将数据库的配 ...
- android7.x Launcher3源代码解析(3)---workspace和allapps载入流程
Launcher系列目录: 一.android7.x Launcher3源代码解析(1)-启动流程 二.android7.x Launcher3源代码解析(2)-框架结构 三.android7.x L ...
- MyBatis官方教程及源代码解析——mapper映射文件
缓存 1.官方文档 MyBatis 包括一个非常强大的查询缓存特性,它能够非常方便地配置和定制. MyBatis 3 中的缓存实现的非常多改进都已经实现了,使得它更加强大并且易于配置. 默认情况下是没 ...
- Spring使用外部的配置文件
在使用Spring做web项目的时候,通常会使用到数据库的连接信息 jdbcUrl driverClass username password 那么应该如何使用这些属性呢? 如在Spring中使用数据 ...
- volley源代码解析(七)--终于目的之Response<T>
在上篇文章中,我们终于通过网络,获取到了HttpResponse对象 HttpResponse是android包里面的一个类.然后为了更高的扩展性,我们在BasicNetwork类里面看到.Volle ...
- Android xUtils3源代码解析之网络模块
本文已授权微信公众号<非著名程序猿>原创首发,转载请务必注明出处. xUtils3源代码解析系列 一. Android xUtils3源代码解析之网络模块 二. Android xUtil ...
随机推荐
- Elasticsearch_Lucene基础
Lucene基本概念 文档(document):索引与搜索的主要载体,它包含一个或多个字段,存放将要写入索引的或将从索引搜索出来的数据. 字段(field):文档的一个片段,它包含字段的名称和字段的内 ...
- (转)Arcgis for javascript实现百度地图ABCD marker的效果
概述: 在我的博客中,有一篇相关的文章,这段时间,有很多人问我求源码,只是时间过去已长,源代码已找不到,乘着这个9.3放假,又重新实现了下,并相关代码做了优化,在此贴出来,方便大家使用. 相关文章地址 ...
- ionic错误
1. 问题:Error: read ECONNRESET 启动使用ionic serve启动服务器之后只要一刷新界面就会导致服务器关闭,报的错误如下: events.js:136 throw er; ...
- CAD从线型文件加载线型记录(com接口)
主要用到函数说明: _DMxDrawX::LoadLinetypeFromFile 从线型文件加载线型记录,详细说明如下: 参数 说明 BSTR pszLinetypeFile 线型文件名,支持htt ...
- Linux快速入门教程-进程管理ipcs命令学习
使用Linux系统必备的技能之一就是Linux进程管理,系统运行的过程正是无数进程在运行的过程.这些进程的运行需要占用系统的内存等资源,做好系统进程的管理,对于我们合理分配.使用系统资源有非常大的意义 ...
- Django - 路由对应关系
1.对url路由关系命名,以后可以根据此名称,生成自己想要的url urls.py中: url('^fdsaafdf(\d+)/',views.index,name='indexx') url('^f ...
- Python MongoDB 教程
基于菜鸟教程实际操作后总结而来 Python MongoDB MongoDB 是目前最流行的 NoSQL 数据库之一,使用的数据类型 BSON(类似 JSON). MongoDB 数据库安装与介绍可以 ...
- 直流电机PWM调速系统中控制电压非线性研究_控制元件_工业自动化控制_文章
直流电机PWM调速系统中控制电压非线性研究_控制元件_工业自动化控制_文章_e-works数字化企业网 http://articles.e-works.net.cn/Component/Article ...
- VS单元测试"未能加载文件或程序集,或它的某一个依赖项"
Autofac.Core.DependencyResolutionException : An error occurred during the activation of a particular ...
- Keil-MDK编译完成后代码大小
Code 代表执行的代码,程序中所有的函数都位于此处. RO-data 代表只读数据,程序中所定义的全局常量数据和字符串都位于此处. RW-data 代表已初始化的读写数据,程序中定义并且初始化的全局 ...