1.3浅谈Spring(IOC容器的实现)
这一节我们来讨论IOC容器到底做了什么。
还是借用之前的那段代码
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
Car car =app.getBean(Car.class);
System.out.println(car.getBrand()+","+car.getDesc());
这里ClassPathXmlApplicationContext是如何加载beans.xml的呢?
它到底做了哪些事情?
1.资源定位 2.资源加载 3.资源注册
再次之前,补充一点:SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition(Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射)来描述的。
IOC初始化
现在我们通过源码来分析并验证:
首先进入它的构造方法,这个构造方法调用其他的构造方法
没错,就是这个方法啦,参数跟上图一样。这里有三个方法:
super();
setConfigLocation();
refresh();
来看一下这三个方法:
1、资源定义
①super();//资源加载器的配置
执行的是AbstractApplicationContext的构造方法
这里的this,就是ClassPathXmlApplicationContext,它间接的继承自AbstractApplication Context,而AbstractApplicationContext又继承了DefaultResourceLoader,故它本身就是个ResourceLoader
②setConfigLocation()//资源定位
处理文件路径为一个字符串的情况
处理多个多个资源文件字符串数组
StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS)
//String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
这个方法是用来解析我们给的location,因为在创建ClassPathXmlApplicationContext时,我们可以传入多个Xml的配置,并用分号隔开。如:
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("beans.xml;spring.xml");
这时候就需要来解析这个String了,当然这个String有多种写法,不止用分号这么简单。所以需要来解析一下。
this.configLocations[i] = resolvePath(locations[i]).trim();
这个resovlePath是用来解析一些占位符的,由于这些文件可能是 classpath , filesystem ,或者是 URL 网络资源, servletContext 等。所以可能会存在占位符。
具体的解析过程在PropertyPlaceholderHelper的parseStringValue中。
2、资源加载
AbstractApplicationContext的refresh()是一个模版方法,它就是对IOC容器进行初始化并且对资源进行载入。其中obtainFreshBeanFactory()就是对"Bean"资源加载的关键。
startupShutdownMonitor用于刷新和销毁的同步标记
①prepareRefresh()//准备刷新
准备此上下文以进行刷新,设置其启动日期和活动标志以及执行属性源的任何初始化。
②obtainFreshBeanFactory()//资源加载
先看refreshBeanFactory();//刷新BeanFactory
判断是否存在BeanFacotry(即IOC容器),如果存在就销毁,因为IOC容器是单例的。只能存在一个。
这里createBeanFactory创建的是一个默认的DefaultListableBeanFactory
customizeBeanFactory();//初始化工厂参数
自定义此上下文使用的内部bean工厂。 为每次refresh()尝试调用。默认实现应用此上下文的“allowBeanDefinitionOverriding”和“allowCircularReferences”设置(如果已指定), 可以在子类中重写以自定义任何DefaultListableBeanFactory的设置。
loadBeanDefinitions();//加载BeanDefinitions
这里调用的是AbstractXmlApplicationContext的loadBeanDefinitions方法
这里传入了RourceLoader的资源加载器为ClassPathXmlApplicationContext
真正执行是重写的方法
继续执行重写方法
真正执行的重写方法:
图-X
这里首先获取getResourceLoader即是获取ClassPathXmlApplication,之前说过它继承自AbstractApplicationContext,而AbstractApplicationContext又继承了DefaultResource Loader
IOC容器是如何取到资源的呢?
这里的getResourceLoader();//取资源加载器
//获取资源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
这里locationPattern用来区分是以多资源文件的形式,还是单资源文件的形式(就像Spring跟SpringMVC同时使用时的情况).
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
这里走else分支的else分支(获取单个资源)
return new Resource[] {getResourceLoader().getResource(locationPattern)};
getResourceLoader()即获取资源加载器,这里的就是ClassPathXmlApplicationContext,
getResource()//获取资源
这里仍然走的是else分支,执行getResourceByPath(location);区分是从 本地,还是URL,还是其他的方式来配置的。
最终执行ClassPathResource的构造方法
最终得到的resource如下图:
在获取到了Resource之后,我们继续回到图-X,看它接下来的操作
int loadCount = loadBeanDefinitions(resources);
最终执行XmlBeanDefinitionReader的loadBeanDefinitions方法:
/**
*从指定的XML文件加载bean定义。
* @param encodedResource XML文件的资源描述符,
*允许指定用于解析文件的编码
* @return找到的bean定义数
* @throws BeanDefinitionStoreException在加载或解析错误的情况下
*/
3、资源注册
在注册之前需要将读取到的resource转换成BeanDefinitions
这里获取资源的输入流,并执行真正执行的方法doLoadBeanDefinitions()
/**
*实际上从指定的XML文件加载bean定义。
* @param inputSource要读取的SAX InputSource
* @param资源XML文件的资源描述符
* @return找到的bean定义数
* @throws BeanDefinitionStoreException在加载或解析错误的情况下
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
这里doLoadDocument(inputSource,resource)将资源文件转换成DOM对象
具体转换过程这里就不进行细看了,主要使用JAXP。
转换成DOM之后需要做的就是将DOM转换成Spring能够识别的数据结构BeanDefinitions
这里的BeanDefinitionDocumentReader用于解析实际的DOM文档。
这里执行DefaultBeanDefinitionDocumentReader的registerBeanDefinitions
doRegisterBeanDefinitions();是真正的注册beanDefinitions的方法
这里有一个用来帮助解析的类:
BeanDefinitionParserDelegate
官方给的解释是:用于解析XML bean定义的有状态委托类。 旨在供主解析器和任何扩展BeanDefinitionParsers或BeanDefinitionDecorators使用。
之后是对Spring命名空间的一些检测。
最后才是真正的解析BeanDefinitions——parseBeanDefinitions
/**
*解析文档中根级别的元素:
*“import”,“alias”,“bean”。
* @param root文档的DOM根元素
*/
这里补充一点XML的知识
xml文档 —————-> Document对象 代表整个xml文档
节点 —————>Node对象 父类
标签节点 —————> Element对象 子类
属性节点 —————> Attribute对象 子类
文本节点 —————>Text对象 子类
NodeList ---------->节点列表集合(Node的集合)
先看这个默认命名空间的解析方法:
parseDefaultElement();
这里分别对<import> <alias> <bean><beans> 用各自的方法进行解析
这里的顺序也是比较讲究
先查看是否有<import>标签,如果有可以先引入其他资源文件到IOC容器中。
然后查看<alias>标签,如果有先引入别名到IOC容器中(后面会根据是否有id将别名赋值给bean)
然后查看<bean>标签
最后查看<beans>,可能<beans>里引入了其他<bean>,故在最后
这里我们查看对于 <bean>标签的解析:
processBeanDefinition();
/**
*处理给定的bean元素,解析beanDefinition
*并在注册表中注册。
*/
首先解析BeanDefinition的基本属性:
BeanDefinitionParserDelegate里的parseBeanDefinitionElement();
1.3浅谈Spring(IOC容器的实现)的更多相关文章
- 浅谈Spring的两种配置容器
浅谈Spring的两种配置容器 原文:https://www.jb51.net/article/126295.htm 更新时间:2017年10月20日 08:44:41 作者:黄小鱼ZZZ ...
- 1.1浅谈Spring(一个叫春的框架)
如今各种Spring框架甚嚣尘上,但是终归还是属于spring的东西.所以在这里,个人谈一谈对spring的认识,笔者觉得掌握spring原理以及spring所涉及到的设计模式对我们具有极大的帮助.我 ...
- 浅谈Spring中的Quartz配置
浅谈Spring中的Quartz配置 2009-06-26 14:04 樊凯 博客园 字号:T | T Quartz是一个强大的企业级任务调度框架,Spring中继承并简化了Quartz,下面就看看在 ...
- Spring IOC容器创建bean过程浅析
1. 背景 Spring框架本身非常庞大,源码阅读可以从Spring IOC容器的实现开始一点点了解.然而即便是IOC容器,代码仍然是非常多,短时间内全部精读完并不现实 本文分析比较浅,而完整的IOC ...
- 详解Spring IoC容器
一.Spring IoC容器概述 1.依赖反转(依赖注入):依赖对象的获得被反转了. 如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码的高度耦合和可测试性的降低,这对复杂的面向对象系统的 ...
- 造轮子:实现一个简易的 Spring IoC 容器
作者:DeppWang.原文地址 我通过实现一个简易的 Spring IoC 容器,算是入门了 Spring 框架.本文是对实现过程的一个总结提炼,需要配合源码阅读,源码地址. 结合本文和源码,你应该 ...
- Spring IoC容器的初始化过程
Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...
- 【Spring】非Spring IOC容器下获取Spring IOC上下文的环境
前言 在Spring Web项目中,有些特殊的时候需要在非Spring IOC容器下获取Spring IOC容器的上下文环境,比如获取某个bean. 版本说明 声明POM文件,指定需引入的JAR. & ...
- 学习Spring(一) 实例化Spring IoC容器
实例化Spring IoC容器 1,读取其配置来创建bean实例 2,然后从Spring IoC容器中得到可用的bean实例 Spring提供两种IoC容器实现类型 a,一种为bean工厂 b,应用程 ...
随机推荐
- 宋宝华:关于Ftrace的一个完整案例【转】
Ftrace简介 Ftrace是Linux进行代码级实践分析最有效的工具之一,比如我们进行一个系统调用,出来的时间过长,我们想知道时间花哪里去了,利用Ftrace就可以追踪到一级级的时间分布. Ftr ...
- JPA之@Transient
java 的transient关键字的作用是需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中. 使用示例: ...
- Linux IO 模型
Linux 中主要有五种IO模式:阻塞IO, 非阻塞IO, IO 多路复用,信号驱动IO和异步IO; 如果从同步非同步,阻塞非阻塞角度来看,又可以分为:同步阻塞IO, 同步非阻塞IO,异步阻塞IO和异 ...
- Java中console类的简单用法
Java.io.Console 只能用在标准输入.输出流未被重定向的原始控制台中使用,在 Eclipse 或者其他 IDE 的控制台是用不了的. import java.io.Console; pub ...
- MySql 学习之路-高级2
目录: 1.约束 2.ALTER TABLE 3.VIEW 1.约束 说明:SQL约束用于规定表中的数据规则,如果存在违反约束的数据行为,行为会被约束终止,约束可以在建表是规定,也可以在建表后规定,通 ...
- IDEA+快捷键
格式化代码:ctrl+alt+L IDEA快捷键管理:https://blog.csdn.net/h8178/article/details/78328097 (duplicate:为复制上一行)
- Windows10下安装Oracle 11g 64位的详细步骤
直接附上我整理后的Word版<Windows10下安装Oracle 11g 64位的详细步骤>下载地址,提取码:9vak. 参考文献: 1.Win10 64位系统下安装Oracle11g详 ...
- THUWC2019:Reach out
竟然还有机会去THUWC!!! 不过没有上WC线感觉有点可惜-- Day -INF~Day -2 考完NOIP两周滚回来被神仙们吊打 先是做专题,为什么会选到构造啊(ノ`Д)ノ 我构造专题有7道题留作 ...
- 终于有人把“TCC分布式事务”实现原理讲明白了!
之前网上看到很多写分布式事务的文章,不过大多都是将分布式事务各种技术方案简单介绍一下.很多朋友看了还是不知道分布式事务到底怎么回事,在项目里到底如何使用. 所以这篇文章,就用大白话+手工绘图,并结合一 ...
- Elastic Stack-Elasticsearch使用介绍(一)
一.前言 Elasticsearch对外提供RESTful API,下面的演示我们主要使用Postman,进行一系列的Demo演示,这款工具方便各位前端大大或者对接口调试的神器: 安装过于简单 ...