Spring IOC 启动过程
1. 引言
本篇博文主要介绍 IOC 容器的启动过程,启动过程分为两个步骤,第一个阶段是容器的启动阶段,第二个阶段是 Bean 实例化阶段,这两个阶段各自需要执行的步骤如下图,接下来会一一介绍。
需要注意的是,在 Spring 中,最基础的容器接口方法是由 BeanFactory 定义的,而 BeanFactory 的实现类采用的是 延迟加载,也就是说,容器启动时,只会进行第一个阶段的操作, 当需要某个类的实例时,才会进行第二个阶段的操作。而 ApplicationContext(另一个容器的实现类)在启动容器时就完成了所有初始化,这就需要更多的系统资源,我们需要根据不同的场景选择不同的容器实现类。
2. 容器启动阶段
2.1 BeanDefinitionRegistry 介绍
在介绍如何加载配置文件之前,先了解一些基础知识。BeanFactory 只是一个接口,它需要一个实现类,DefaultListableBeanFactory 就是一个比较常用的实现类,它还实现了 BeanDefinitionRegisitry 接口,该接口在容器中担任 Bean 注册的角色。
打个比方,BeanDefinitionRegistry 就像图书馆上的书架,所有的书是放在书架上的。虽然还书借书都是跟图书馆(也就是 BeanFactory,或者说 BookFactory)打交道,但书架才是图书馆存放各类图书的地方。所以,书架对于图书馆来说,就是他的 BookDefinitionRegistry。
而每一本书都应该有自己唯一的标识,在容器中每个实例也应该有这样的标识,这些标识是由 BeanDefinition 存储的。它负责保存对象的所有必要信息,例如对象的 class 类型、是否是抽象类、构造方法参数以及其他属性等等,当客户端向 BeanFactory 请求相应对象时,BeanFactory 会通过这些信息返回一个完备可用的对象实例。
2.2 加载配置文件
接下来介绍如何加载配置文件。IOC 的理念是 Don't call us, we will call you。当一个类中需要另外一个类的实例时,我们并不用手动去创建,IOC 容器会帮我们完成这个任务,但我们需要告诉它哪些类之间存在依赖,例如 A 类中依赖的是哪个类,B 类依赖的类是在哪个包下面,而这些信息我们可以使用注解或者配置文件的方式告诉 IOC 容器。
这里主要讲下读取配置 IOC 是如何读取配置文件的。IOC 容器读取配置文件的接口为 BeanDefinitionReader,它会根据配置文件格式的不同给出不同的实现类,将配置文件中的内容读取并映射到 BeanDefinition 中,整个过程可以通过如下代码表示:
public static void main(String[] args){
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory container = (BeanFactory)bindViaPropertiesFile(beanRegistry);
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
}
public static BeanFactory bindViaPropertiesFile(BeanDefinitionRegistry registry){
BeanDefinitionRegistry beanRegistry = <某个 BeanDefinitionRegistry 实现类,通常为 DefaultListableBeanFactory>;
BeanDefinitionReader beanDefinitionReader = new BeanDefinitionReaderImpl(beanRegistry);
// 读取配置文件的核心方法
beanDefinitionReader.loadBeanDefinitions("配置文件路径");
return (BeanFactory)registry;
}
2.3 解析配置文件
在上面读取配置文件的步骤中,仅仅是将 <bean> 中的属性值读取出来,这只是一些字符串,最终应用程序却是由各种类型的对象实例构成的,我们需要将这些字符串转换成类信息,这个转化过程就需要定义一个规则,这些规则是由 PropertyEditor 定义,由 CustomEditorConfigurer 帮我们传递给 Spring 容器。经过 PropertyEditor 定义的规则将字符串转换为对应的类型信息之后并存储在 BeanDefinition 中,交给 BeanDefinitionRegistry 管理,容器的启动过程就完成了。其初始化阶段可以用一张图来表示:
3. Bean 实例化阶段
Bean 实例化过程如下图:
3.1 Bean 的实例化
容器在内部实现 Bean 实例化时,采用 策略模式 来决定使用何种方式初始化 bean 实例。InstantiationStrategy 定义了实例化策略的接口,SimpleInstantiationStrategy 继承了它,主要通过 反射 来实现对象的实例化。CglibSubclassingInstantiation 继承了 SimpleInstantiationStrategy 以反射方式实例化的功能,并且还有 CGLIB 动态字节码 生成实例的功能。容器默认采用后者实现。
但是需要注意的是,InstantiationStrategy 实例化对象后并没有直接将对象返回,而是用 BeanWrapper 进行包装,方便后续对此实例进行 属性的设置。其设置的依据是通过上面所讲的 PropertyEditor 接口,在第一步构造完成对象之后,Spring 会根据对象实例构造一个 BeanWrapperImpl 实例,然后将之前 CustomEditor-
Configurer 注册的 PropertyEditor 复制一份给 BeanWrapperImpl 实例这样,当 BeanWrapper 转换类型、设置对象属性值时,就不会无从下手了。
3.2 BeanPostProcessor
当对象实例化完成之后,会将对象实例传到 BeanPostProcessor,这个接口的定义如下:
public interface BeanPostProcessor{
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
这个接口为我们扩展对象实例的行为提供了极大的便利,其中 postProcessBeforeInitialization 对应的就是上图中 BeanPostProcessor 的前置处理,postProcessAfterInitialization 对应的就是 上图中 BeanPostProcessor 的后置处理。Spring 的 AOP 就是使用 BeanPostProcessor 来为对象生成相应的代理对象。
3.3 Bean 的初始化
在对象实例话过程调用 BeanPostProcessor 的前置处理 之后,会接着检测对象是否实现了 InitializingBean 接口,如果是,则会调用该接口的 afterPropertiesSet 方法进一步调整对象实例的状态。但是,如果仅仅为了做一个初始化动作而去实现一个接口这样未免有点小题大做,因此 Spring 有提供了另一种方法,就是在 <bean> 中配置 init-method 属性,指定一个方法做对象初始化前的操作。到了这个步骤,对象实例化也快接近尾声了。
3.4 Bean 的销毁
当所有的一切,该设置的设置,该注入的注入,该调用的调用之后,容器将会检查 singleton 类型的 bean 实例,看起是否实现了 DisposableBean 接口,或者查看对应的 bean 定义是否通过 <bean> 的 destroy-method 属性指定了自定义的对象销毁方法。如果是,就会为该实例注册一个用于对象销毁的回调(Callback),以便在这些 singleton 类型的对象实例销毁之前,执行销毁逻辑。至此,Bean 对象的实例化阶段也完成了。
4. 总结
本篇博文主要是对 Spring IOC 容器初始化过程中涉及到的重点接口进行了讲解,对于整个启动过程并没有做一个清晰的梳理,有需要还是建议解析其他的博客以前学习,我觉得这样效果更佳。
Spring IOC 启动过程的更多相关文章
- spring的启动过程就是创建ioc容器的过程
1. spring简介 spring的最基本的功能就是创建对象及管理这些对象之间的依赖关系,实现低耦合.高内聚.还提供像通用日志记录.性能统计.安全控制.异常处理等面向切面的能力,还能帮我们管理最头疼 ...
- Spring MVC启动过程(1):ContextLoaderListener初始化
此文来自https://my.oschina.net/pkpk1234/blog/61971 (写的特别好)故引来借鉴 Spring MVC启动过程 以Tomcat为例,想在Web容器中使用Spirn ...
- 转:spring的启动过程-spring和springMVC父子容器的原理
要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的.spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程. s ...
- Web环境中Spring的启动过程
1.spring不但可以在JavaSE环境中应用,在Web环境中也可以广泛应用,Spring在web环境中应用时,需要在应用的web.xml文件中添加如下的配置: …… <context-par ...
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- Spring Boot启动过程及回调接口汇总
Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...
- Spring Boot启动过程(三)
我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...
- Spring Boot启动过程(一)
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
随机推荐
- bzoj4397[Usaco2015 dec]Breed Counting*
bzoj4397[Usaco2015 dec]Breed Counting 题意: 给定一个长度为N的序列,每个位置上的数只可能是1,2,3中的一种.有Q次询问,每次给定两个数a,b,请分别输出区间[ ...
- 太实用了!自己动手写软件——SSH、FTP和SQL server的密码破解
我们的密码破解工具一共分为如下六个部分,前面四个部分我们都有在之前的文章中介绍过了 用户图形界面——GUI编程 密码字典获取——Excel文件读取 数据库类——MySQL.Oracle和SQL ser ...
- linux管理防火墙
操作系统环境:CentOS Linux release 7.0.1406(Core) 64位CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙步骤. 1.关闭f ...
- 数据聚合与分组操作知识图谱-《利用Python进行数据分析》
所有内容整理自<利用Python进行数据分析>,使用MindMaster Pro 7.3制作,emmx格式,源文件已经上传Github,需要的同学转左上角自行下载或者右击保存图片. 其他章 ...
- 【Python学习笔记六】获取百度搜索结果以及百度返回“百度安全验证”问题解决
1.获取百度搜索结果页面主要是修改百度搜索url中的参数实现,例如查询的关键字为wd: 举例:https://www.baidu.com/s?wd=python",这样就可以查询到‘pyth ...
- 【JVM之内存与垃圾回收篇】对象实例化内存布局与访问定位
对象实例化内存布局与访问定位 从各自具体的内存分配上来讲 new 的对象放在堆中 对象所属的类型信息是放在方法区的 方法当中的局部变量放在栈空间 这 new 的对象怎么把三块粘合到一起 就是这章的内容 ...
- 水题-------判断Digit Generator
题目链接:https://vjudge.net/problem/UVA-1583 题意:给出一个数N,判断最小的数x使x+(x各位数字的和)=N 题解:这是一个暴力求解题,不过有技巧,x各位数字的和最 ...
- 图表可视化seaborn风格和调色盘
seaborn是基于matplotlib的python数据可视化库,提供更高层次的API封装,包括一些高级图表可视化等工具. 使用seaborn需要先安装改模块pip3 install seaborn ...
- laravel5.5 安装
环境要求 PHP >= 7.0.0 PHP OpenSSL 扩展 PHP PDO 扩展 PHP Mbstring 扩展 PHP Tokenizer 扩展 PHP XML 扩展 通过 Larave ...
- ken桑带你读源码之scrapy downloadermiddlewares
downloadermiddlewares 文件夹是下载中间件 其中 process_request 还没请求时的处理函数 process_response 请求之后的处理函数 chunke ...