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 ...
随机推荐
- tolua-ToLua#暖更新
"重写"C#函数的Lua函数要访问C#类对象的没有wrap进Lua环境的私有数据成员.私有方法的时候,目前只能使用静态反射. 关于全Lua开发.全C#开发的问题.全Lua开发可能或 ...
- 绕过CDN查找真实IP方法
0x01 验证是否存在CDN 方法1: 很简单,使用各种多地 ping 的服务,查看对应 IP 地址是否唯一,如果不唯一多半是使用了CDN, 多地 Ping 网站有:http://ping.china ...
- for语句例题:编写程序FooBizBaz.java,从1循环到150并在每行打印一个值
/** * 编写程序FooBizBaz.java,从1循环到150并在每行打印一个值, * 另外在每个3的倍数行上打印出"foo",在每个5的倍数行上打印"biz&quo ...
- P1100 高低位切换
这个题很简单 直接用左移位(<<)和右移位(>>)就可以过了 #include<iostream> #include<cstdio> using nam ...
- abp vnext 开发快速入门 3 实现权限控制
上篇讲了abp vnext 实现了简单的增加操作的例子.删除更新查询基本类似,这里就不讲了,接下来说下如何实现角色权限控制. 再说之前,先说下如果想更加透彻的理解abp vnext的权限控制,最好是先 ...
- 题解 洛谷 P6142 【[USACO20FEB]Delegation P】
和赛道修建类似,先对\(k\)进行二分,将最值问题转化为判定问题. 在判定一个\(k\)是否合法时,贪心去考虑,一个节点下面的若干条链在合并时,一条链肯定和另一条使它合并后恰好满足长度限制的链合并最优 ...
- 遍历map的6种方式
1,平时开发中对map的使用很多,然后发现了很多map可能存在的各种问题:如HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize ...
- WinForm微信扫码登录
源码还需优化,不喜勿喷. 微信官方文档 : https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat ...
- 前缀和线性基HDU6579
Operation 题解:看到区间最大异或和,首先想到的是线性基: 线性基可以处理的操作是: 在数列末尾插入一个数 查询全局的子集异或最大值 由于线性基的长度很短,因此我们可以将数列所有前缀的线性基保 ...
- CSRF与XSS攻击的原理与防范
CSRF 1.概念与原理 CSRF,跨站请求伪造,攻击方伪装用户身份发送请求从而窃取信息或者破坏系统.例如: 用户访问A网站登陆并生成了cookie,再访问B网站,如果A网站存在CSRF漏洞,此时B网 ...