一, Spring IoC 容器概述
1. 依赖反转 (依赖注入): 依赖对象的获得被反转了.
如果合作对象的引用或依赖关系的管理由具体对象来完成, 会导致代码的高度耦合和可测试性的降低, 这对复杂的面向对象系统的设计是非常不利的.
在 Spring 中, IoC 容器是实现依赖控制反转这个模式的载体, 它可以在对象生成或者初始化时直接将数据注入到对象中, 也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖. 这种依赖是可以递归的, 对象被逐层注入.
关于如何反转对依赖的控制, 把控制权从具体业务对象中转交到平台或者框架中, 是降低面向对象系统设计复杂性和提高面向对象系统可测试性的一个有效的解决方案. 它促进 IoC 设计模式的发展, 是 IoC 容器要解决的核心问题.
具体依赖注入的主要实现方式: 接口注入 (Type 1 IoC),setter 注入 (Type 2 IoC), 构造器注入 (Type 3 IoC), 在 Spring 的 IoC 设计中, setter 注入和构造器注入是主要的注入方式, 相对而言, 使用 Spring 时 setter 注入是常见的注入方式, 而且为了防止注入异常, Spring IoC 容器还提供了对特定依赖的检查.
二, IoC 容器系列的设计与实现: BeanFactory 和 ApplicationContext
BeanFactory 简单容器系列: 这系列容器只实现了容器的最基本功能;
ApplicationContext 高级容器系列: ApplicationContext 应用上下文, 作为同期的高级形态存在. 应用上下文在简单容器的基础上, 增加了许多面向框架的特性, 同时对应用环境做了许多适配.
IoC 容器是用来管理对象依赖关系的, 对 IoC 容器来说, BeanDefinition 就是对依赖反转模式中管理的对象依赖关系的数据抽象, 也是容器实现依赖反转功能的核心数据结构, 依赖反转功能都是围绕对这个 BeanDefinition 的处理来完成的.
上图是 IoC 容器的接口设计图, 从图中我们可以看到, IoC 容器主要有两种设计路径:
1. 从接口 BeanFactory 到 HierarchicalBeanFactory, 再到 ConfigurableBeanFactory, 是一条主要的 BeanFactory 设计路径. 在这条接口设计路径中, BeanFactory 接口定义了基本的 IoC 容器规范. 在这个接口定义中, 包括了 getBean() 这样的 IoC 容器的基本方法 (通过这个方法可以从容器中取得 Bean).
2. 第二条接口设计主线是, 以 ApplicationContext 应用上下文接口为核心的接口设计, 这里涉及的主要接口设计有, 从 BeanFactory 到 ListableBeanFactory, 再到 ApplicationContext, 再到我们常用的 webApplicationContext 或者 ConfigurableApplicationContext 接口. 对于 ApplicationContext 接口, 它通过继承 MessageSource,ResourceLoader,ApplicationEventPublisher 接口, 在 BeanFactory 简单 IoC 容器的基础上添加了许多对高级容器的特性支持.
(一),BeanFactory
BeanFactory 接口定义了 IoC 容器最基本的形式, 并且提供了 IoC 容器所应该遵守的最基本的服务契约, 同时, 这也是我们使用 IoC 容器所应遵守的最底层和最基本的编程规范, 这些接口定义勾出了 IoC 的基本轮廓.
BeanFactory 和 FactoryBean 是在 Spring 中使用频率很高的类. 它们在拼写上非常相似. 一个是 Factory, 也就是 IoC 容器或者对象工厂; 一个是 Bean. 在 Spring 中, 所有的 Bean 都是由 BeanFactory(也就是 IoC 容器) 来进行管理的. 但对 FactoryBean 而言, 这个 Bean 不是简单的 Bean, 而是一个能产生或者修饰对象生成的工厂 Bean, 它的实现与设计模式中的工厂模式和修饰器模式类似.
BeanFactory 源码:
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by Fernflower decompiler)
- //
- package org.springframework.beans.factory;
- import org.springframework.beans.BeansException;
- import org.springframework.core.ResolvableType;
- import org.springframework.lang.Nullable;
- public interface BeanFactory {
- String FACTORY_BEAN_PREFIX = "&";
- Object getBean(String var1) throws BeansException;
- <T> T getBean(String var1, @Nullable Class<T> var2) throws BeansException;
- Object getBean(String var1, Object... var2) throws BeansException;
- <T> T getBean(Class<T> var1) throws BeansException;
- <T> T getBean(Class<T> var1, Object... var2) throws BeansException;
- boolean containsBean(String var1);
- boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
- boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
- boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
- boolean isTypeMatch(String var1, @Nullable Class<?> var2) throws NoSuchBeanDefinitionException;
- @Nullable
- Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
- String[] getAliases(String var1);
- }
通过 BeanFactory 接口的定义, 用户可以执行以下操作:
1. 通过接口方法 getBean 获取 Bean, 还可以通过参数方法对 Bean 类型进行检查;
2. 通过接口方法 containsBean 让用户能够判断容器是否含有制定名字的 Bean;
3. 通过接口方法 isSingleton 来查询指定名字的 Bean 是否是 Singleton 类型的 Bean. 对于 Singleton 属性, 用户可以在 BeanDefinition 中指定;
4. 通过接口方法 isPrototype 来查询指定名字的 Bean 是否是 prototype 类型的. 与 Singleton 属性一样, 这个属性也可以由用户在 BeanDefinition 中指定;
5. 通过接口方法 isTypeMatch 来查询指定了名字的 Bean 的 Class 类型是否是特定的 Class 类型. 这个 Class 类型可以由用户指定;
6. 通过接口方法 getType 来查询指定名字的 Bean 的 Class 类型;
7. 通过接口方法 getAliases 来查询指定了名字的 Bean 的所有别名, 这些别名都是用户在 BeanDefinition 中定义的;
这些定义的接口方法勾画出了 IoC 容器的基本特性.
为了更清楚地了解 BeanFactory 作为容器的工作原理, 我们来看一下 BeanFanctory 的一个实现类 XmlBeanFactory 的源代码:
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by Fernflower decompiler)
- //
- package org.springframework.beans.factory.xml;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.beans.factory.support.DefaultListableBeanFactory;
- import org.springframework.core.io.Resource;
- /** @deprecated */
- @Deprecated
- public class XmlBeanFactory extends DefaultListableBeanFactory {
- private final XmlBeanDefinitionReader reader;
- public XmlBeanFactory(Resource resource) throws BeansException {
- this(resource, (BeanFactory)null);
- }
- public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
- super(parentBeanFactory);
- this.reader = new XmlBeanDefinitionReader(this);
- this.reader.loadBeanDefinitions(resource);
- }
- }
我们看到 XmlBeanFactory 是用了 DefaultListableBeanFactory 作为基类, DefaultListableBeanFactory 是很重要的一个 IoC 实现, 在其他 IoC 容器中, 比如 ApplicationContext, 其实现的基本原理和 XmlBeanFactory 一样, 也是通过持有或者扩展 DefaultListableBeanFactory 来获得基本的 IoC 容器的功能的.
参考 XmlBeanFactory 的实现, 我们以编程的方式使用 DefaultListableBeanFactory. 从中我们可以看到 IoC 容器使用的一些基本过程.
- package com.xyfer.controller;
- import org.springframework.beans.factory.support.DefaultListableBeanFactory;
- import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
- import org.springframework.core.io.ClassPathResource;
- public class IoCDemo {
- public static void main(String[] args) {
- ClassPathResource res = new ClassPathResource("demo.xml");
- DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
- XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
- reader.loadBeanDefinitions(res);
- }
- }
这样, 我们就可以通过 factory 对象来使用 DefaultListableBeanFactory 这个 IoC 容器来. 在使用 IoC 容器时, 需要如下几个步骤:
1. 创建 IoC 配置文件的抽象资源, 这个抽象资源包含了 BeanDefinition 的定义信息;
2. 创建一个 BeanFactory, 这里使用 DefaultListableBeanFactory;
3. 创建一个载入 BeanDefinition 的读取器, 这里使用 XmlBeanDefinitionReader 来载入 xml 文件形式的 BeanDefinition, 通过一个回调配置给 BeanFactory;
4. 从定义好的资源位置读入配置信息, 具体的解析过程由 XmlBeanDefinitionReader 来完成. 完成整个载入和注册 Bean 定义之后, 需要的 IoC 容器就建立起来了. 这个时候就可以直接使用 IoC 容器了.
(二),ApplicationContext
ApplicationContext 除了提供 BeanFactory 提供的容器的基本功能外, 还为用户提供了以下的附加服务, 所以说 ApplicationContext 是一个高级形态意义的 IoC 容器.
从 ApplicationContext 继承关系中, 可以看到 ApplicationContext 在 BeanFactory 的基础上通过实现不同的接口而添加不同的附加功能.
1. 支持不同的信息源. ApplicationContext 扩展了 MessageSource 接口, 这些信息源的扩展功能可以支持国际化的实现, 为开发多语言版本的应用提供服务.
2. 访问资源. 这一特性体现在对 ResourceLoader 和 Resource 的支持上, 这样我们可以从不同的地方得到 Bean 定义资源.
3. 支持应用事件. 继承了接口 ApplicationEventPublisher, 从而在上下文中引入了事件机制. 这些事件和 Bean 的生命周期的结合为 Bean 的管理提供了便利.
4. 在 ApplicationContext 中提供的附加服务. 这些服务使得基本 IoC 容器的功能更丰富. 一般建议在开发应用时使用 ApplicationContext 作为 IoC 容器的基本形式.
三, IoC 容器的初始化过程
简单来说, IoC 容器的初始化是由 refresh() 方法启动的, 这个方法标志 IoC 容器的正式启动. 具体来说, 这个启动包括 BeanDefinition 的 Resource 定位, 载入和注册三个基本过程.
1.Resource 定位过程. Resource 定位指的是 BeanDefinition 的资源定位, 它由 ResourceLoader 通过统一的 Resource 接口来完成, 这个 Resource 对各种形式的 BeanDefinition 的使用都提供来统一的接口. 在文件系统中的 Bean 定义信息可以使用 FileSystemResource 来进行抽象; 在类路径中的 Bean 定义信息可以使用 ClassPathResource 来抽象.
2.BeanDefinition 的载入. 这个载入过程是把用户定义好的 Bean 表示成 IoC 容器内部的数据结构, 而这个容器内部的数据结构就是 BeanDefinition. 具体来说, 这个 BeanDefinition 实际上就是 POJO 对象在 IoC 容器中的抽象, 通过这个 BeanDefinition 定义的数据结构, 使 IoC 容器能够方便地对 POJO 对象也就是 Bean 进行管理.
3. 向 IoC 容器注册这些 BeanDefinition 的过程. 这个过程是通过调用 BeanDefinitionRegistry 接口的实现来完成的. 这个注册过程把载入过程中解析得到的 BeanDefinition 向 IoC 容器进行注册. 通过分析, 我们可以看到, 在 IoC 容器内部将 BeanDefinition 注入到一个 HashMap 中去, IoC 容器就是通过这个 HashMap 来持用这些 BeanDefinition 数据的.
这里谈的是 IoC 容器初始化过程, 这个过程一般不包含 Bean 依赖注入的实现. 在 Spring IoC 的设计中, Bean 定义的载入和依赖注入是两个独立的过程. 依赖注入一般发生在应用第一次通过 getBean 向容器索取 Bean 的时候. 但是又一个例外的配置, 在使用 IoC 容器时有一个预实例化的配置, 通过这个预实例化的配置 (具体来说, 可以通过为 Bean 定义信息中的 lazyinit 属性), 可以对容器初始化过程做一个微小的控制, 从而改变这个被设置了 lazyinit 属性的 Bean 的依赖注入过程. 举例来说, 如果我们对某个 Bean 设置了 lazyinit 属性, 那么这个 Bean 的依赖注入在 IoC 容器初始化时就预先完成了, 而不需要等到整个初始化完成以后, 第一次使用 getBean 时才会触发.
四, IoC 容器的依赖注入
IoC 容器的初始化过程完成的主要工作在 IoC 容器中建立 BeanDefinition 数据映射. 但是在此过程中 IoC 容器并没有对 Bean 的依赖关系进行注入.
当 IoC 容器已经载入了用户定义的 Bean 信息, 容器中的 BeanDefinition 数据已经建立好的前提下, 依赖注入的过程是在用户第一次向 IoC 容器索要 Bean 时触发的, 也就是第一次调用 getBean 的时候触发, 当然也有例外, 就是当在 BeanDefiniton 中设置 lazyinit 属性来让容器完成对 Bean 的预实例化. 这个预实例化实际上也是一个完成依赖注入的过程, 但是这个依赖注入的过程是在初始化的过程中完成的.
getBean 是依赖注入的起点, 之后会调用 createBean,Bean 对象会依据 BeanDefinition 定义的要求生成. createBean 不但生成了需要的 Bean, 还对 Bean 初始化进行了处理, 比如实现了在 BeanDefinition 中的 init-method 属性定义, Bean 后置处理器等. CGLIB 是一个常用的字节码生成器的类库, 它提供了一系列的 API 来提供生成和转换 JAVA 的字节码的功能. 在 Spring AOP 中也使用 CGLIB 对 JAVA 的字节码进行增强. 在 IoC 容器中, Spring 通过默认类 SimpleInstantiationStrategy 类来生成 Bean 对象, 它提供了两种实例化 Java 对象的方法, 一种是通过 BeanUtils, 它使用了 JVM 的反射功能, 一种是通过 CGLIB 来生成.
在实例化 Bean 对象生成的基础上, 接下来就是各种依赖关系的处理. 通过对 BeanDefinition 中的对象, value 值, List,Map 等进行解析, 然后使用反射对属性进行注入.
在 Bean 的创建和对象依赖注入的过程中, 使用递归在上下文体系中查找需要的 Bean 和创建 Bean; 在依赖注入时, 通过递归调用容器的 getBean 方法, 得到当前 Bean 的依赖 Bean, 同时也触发对依赖 Bean 的创建和注入. 在对 Bean 的属性进行依赖注入时, 解析的过程也是递归的过程. 这样, 根据依赖关系, 一层一层地完成 Bean 的创建和注入, 直到最后完成当前 Bean 的创建. 有了这个顶层 Bean 的创建和对它的属性依赖注入的完成, 意味着和当前 Bean 相关的整个依赖链的注入也完成了.
五, IoC 容器的其他相关特性
1.ApplicationContext 和 Bean 的初始化及销毁
Bean 的生命周期
(1)Bean 实例的创建
(2) 为 Bean 实例设置属性
(3) 调用 Bean 的初始化方法
(4) 应用可以通过 IoC 容器使用 Bean
(5) 当容器关闭时, 调用 Bean 的销毁方法
2.lazy-init 属性和预实例化
3.FactoryBean 的实现
4.BeanPostProcessor 的实现
5.autowiring(自动依赖装配) 的实现
配置 autowiring 属性, IoC 容器会根据这个属性的配置, 使用反射自动查找属性的类型或者名字, 然后基于属性的类型或名字来自动匹配 IoC 容器中的 Bean, 从而自动地完成依赖注入.
6.Bean 的依赖检查
Spring 通过依赖检查特性, 帮助应用检查是否所有的属性都已经被正确设置. 在 Bean 定义时设置 dependency-check 属性来指定依赖检查模式即可. 属性可以设置为 none,simple,object,all 四种模式, 默认的模式是 none.
来源: https://www.cnblogs.com/xyfer1018/p/12170569.html