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 中,整个过程可以通过如下代码表示:

  1. public static void main(String[] args){
  2. DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
  3. BeanFactory container = (BeanFactory)bindViaPropertiesFile(beanRegistry);
  4. FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
  5. newsProvider.getAndPersistNews();
  6. }
  7. public static BeanFactory bindViaPropertiesFile(BeanDefinitionRegistry registry){
  8. BeanDefinitionRegistry beanRegistry = <某个 BeanDefinitionRegistry 实现类,通常为 DefaultListableBeanFactory>;
  9. BeanDefinitionReader beanDefinitionReader = new BeanDefinitionReaderImpl(beanRegistry);
  10. // 读取配置文件的核心方法
  11. beanDefinitionReader.loadBeanDefinitions("配置文件路径");
  12. return (BeanFactory)registry;
  13. }

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,这个接口的定义如下:

  1. public interface BeanPostProcessor{
  2. Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  3. Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
  4. }

这个接口为我们扩展对象实例的行为提供了极大的便利,其中 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 启动过程的更多相关文章

  1. spring的启动过程就是创建ioc容器的过程

    1. spring简介 spring的最基本的功能就是创建对象及管理这些对象之间的依赖关系,实现低耦合.高内聚.还提供像通用日志记录.性能统计.安全控制.异常处理等面向切面的能力,还能帮我们管理最头疼 ...

  2. Spring MVC启动过程(1):ContextLoaderListener初始化

    此文来自https://my.oschina.net/pkpk1234/blog/61971 (写的特别好)故引来借鉴 Spring MVC启动过程 以Tomcat为例,想在Web容器中使用Spirn ...

  3. 转:spring的启动过程-spring和springMVC父子容器的原理

    要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的.spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程. s ...

  4. Web环境中Spring的启动过程

    1.spring不但可以在JavaSE环境中应用,在Web环境中也可以广泛应用,Spring在web环境中应用时,需要在应用的web.xml文件中添加如下的配置: …… <context-par ...

  5. Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...

  6. Spring Boot启动过程(七):Connector初始化

    Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...

  7. Spring Boot启动过程及回调接口汇总

    Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...

  8. Spring Boot启动过程(三)

    我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...

  9. Spring Boot启动过程(一)

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...

随机推荐

  1. 关于报错,Whoops! Lost connection to ws://XXX.XXX.XXX.XXX:15684/ws

    昨天,在玩rabbitMQ时候,用stompJS从web连接ranbbitMQ时,报了标题的错误消息! 我把我这个html页面代码贴上,简单得讲,就是断开后,重新连接即可.

  2. 第五章:理解RemoteViews

    RemoteView应该是一种远程View,表示的是一个View结构,他可以在其它进程中显示. 在android中使用场景有两种:通知栏和桌面小部件 5.1 RemoteView的应用 5.1.1 R ...

  3. P.SDA1.DEV - 一个没有服务器的图床

    图床特色 P.SDA1.DEV的愿景是为大家提供一个免费.长期稳定外链分享图片的选择. P.SDA1.DEV的主要特点有: 完全建构在Serverless云服务上,致力于提供(墙外)可用性99.9%的 ...

  4. 异常类throwable

    一.Error 严重错误,系统内部的错误.无法通过处理,只能避免. 二.Exception 使用不当导致,是可以避免的. 异常分类: 1.编译时异常 编译时遇到的异常,若未处理,就会编译失败,必须进行 ...

  5. .Net Core缓存组件(MemoryCache)【缓存篇(二)】

    一.前言 .Net Core缓存源码 1.上篇.NET Core ResponseCache[缓存篇(一)]中我们提到了使用客户端缓存.和服务端缓存.本文我们介绍MemoryCache缓存组件,说到服 ...

  6. Springboot(二)springboot之jsp支持

    参考恒宇少年的博客:https://www.jianshu.com/p/90a84c814d0c springboot内部对jsp的支持并不是特别理想,而springboot推荐的视图是Thymele ...

  7. [leetcode/lintcode 题解] 一致性哈希 II · Consistent Hashing II

    [题目描述] 在 Consistent Hashing I 中我们介绍了一个比较简单的一致性哈希算法,这个简单的版本有两个缺陷: 增加一台机器之后,数据全部从其中一台机器过来,这一台机器的读负载过大, ...

  8. Spring集成Quartz定时任务

    1.导入jar包 2.配置applicationContext.xml文件 <!-- 任务调度1 --> <!-- bean id="simpleJob" cla ...

  9. 使用ATOMac进行Mac自动化测试

    ATOMac简介 atomac是一个支持在mac上做自动化的python库,GitHub地址如下: https://github.com/pyatom/pyatom 安装 # Python2 sudo ...

  10. LQB201808全球变暖 bfs

    #include<iostream> #include<stdio.h> #include<stdlib.h> #include<time.h> #in ...