1 Spring IOC

1.1 IOC 的概念

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。Before,开发人员创建操作对象。Now,容器创建操作对象。控制权的转移带来的好处就是降低了业务对象之间的依赖程度,即实现了解耦。

IoC 是一种思想,而 DI 则是一种具体的技术实现手段。

Spring 框架使用依赖注入来实现 IOC,具体来说只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建、赋值、查找并且管理对象的生命周期都由容器内部实现。

1.2 IOC 作用

  • 管理对象的创建和依赖关系的维护。当对象的创建很复杂时,程序员维护是很头疼的。
  • 解耦,由容器去维护具体对象。
  • 托管了类的整个生命周期,涉及到创建对象、管理对象、装配对象等等。

1.3 IOC 优点

  • IOC 可以将代码量进一步降低。
  • 它使应用容易测试,单元测试不再需要单例和 JNDI 查找机制。
  • 最小的代价和最小的侵入性使松散耦合得以实现。
  • IOC 容器支持加载服务时的饿汉式初始化和懒加载。

1.4 IOC 功能支持

  • 依赖注入
  • 依赖检查
  • 自动装配
  • 支持集合
  • 指定初始化方法和销毁方法
  • 支持回调某些方法(但是需要实现 Spring 接口,略有侵入)

其中,最重要的就是依赖注入,从 XML 的配置上说,即 ref 标签。对应 Spring RuntimeBeanReference 对象。

1.5 DI 的基本原则

应用组件不应该负责查找资源或其他依赖的协作对象,配置对象和查找资源的工作应该由 IOC 容器负责。容器全权负责组件的装配,把符合依赖关系的对象通过 setter 方法或构造器方法传递给需要的对象。

1.6 IOC/DI 的实现方式及区别

最流行的 IOC 实现方式主要包含两种,分别是 setter 方法注入和构造器注入。
构造器注入是容器触发类的构造器来实现。setter 方法通过调用无参构造器或无参静态工厂方法实例化 bean 后,调用 bean 的 setter 方法。
构造器注入没有部分注入,不会覆盖 setter 属性,任意修改都会创建一个新实例,适用于设置很多属性。
setter 注入有部分注入,会覆盖 setter 属性,任意修改不会创建新实例,适用于少量属性。
一般使用构造器注入来实现强制依赖,使用 setter 注入实现可选依赖。

1.7 Spring IoC 的实现机制

工厂模式+反射机制。

1.8 Spring 如何设计 IOC 容器

Spring IoC 容器的设计主要是基于两个接口,一是 BeanFactory 接口的简单容器,另一个是 ApplicationContext 接口的高级容器。

BeanFactory:是 Spring 里面最底层的接口,包含了各种 Bean 的定义,读取 bean 配置文档,管理 bean 的加载、实例化,控制 bean 的生命周期,维护 bean 之间的依赖关系。

ApplicationContext 接口作为 BeanFactory 的子接口,除了提供 BeanFactory 所具有的功能外,还继承了其他多个功能接口,为 BeanFactory 赋予了更高级的 IOC 容器特性。这些功能接口有:MessageSource 支持国际化,ResourcePatternResolver 统一的资源文件访问方式,ApplicationEventPublisher 提供在监听器中注册 Bean 的事件,同时加载多个配置文件,载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的 web 层。

1.9 BeanFactory 和 ApplicationContext 的区别

BeanFactory 和 ApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器。其中 ApplicationContext 是 BeanFactory 的子接口。

一是,提供的功能不同。
BeanFactory:是 Spring 里面最底层的接口,包含了各种 Bean 的定义,读取 bean 配置文档,管理 bean 的加载、实例化,控制 bean 的生命周期,维护 bean 之间的依赖关系。
ApplicationContext 接口作为 BeanFactory 的子接口,除了提供 BeanFactory 所具有的功能外,还继承了其他多个功能接口,为 BeanFactory 赋予了更高级的 IOC 容器特性。这些功能接口有:MessageSource 支持国际化ResourcePatternResolver 统一的资源文件访问方式ApplicationEventPublisher 提供在监听器中注册 Bean 的事件,同时加载多个配置文件载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的 web 层。

二是,加载方式不同。
BeanFactory 采用 延迟加载 的形式来注入 Bean,只有使用到某个 Bean 时,才会对该 Bean 进行实例化。缺点是,不能提早发现配置依赖错误问题,只有在加载该错误 Bean 抛出异常时,才能发现错误。
ApplicationContext 采用一次洗创建所有 Bean 的形式。优点是,一次加载,后续直接调用省时间,顺便有利于检查配置依赖的正确性。缺点是,占内存,并且 Bean 较多时启动慢。

三是,注册方式不同。
BeanFactory 和 ApplicationContext 都支持 BeanPostProcessor、BeanFactoryPostProcessor 的使用。
BeanFactory 需要手动注册,而 ApplicationContext 则是自动注册。

四是,创建方式不同。
BeanFactory 通常以编程的方式被创建。
ApplicationContext 还能以声明的方式创建,如使用 ContextLoader。

一般来说直接使用 ApplicationContext 接口。

1.10 ApplicationContext 的实现类

  • ClassPathXmlApplicationContext:从系统类路径 classpath 下加载一个或多个 xml 配置文件,适用于 xml 配置的方式
  • FileSystemXmlApplicationContext:从系统磁盘下加载一个或多个 xml 配置文件(必须有访问权限)
  • XmlWebApplicationContext:从 web 应用下加载一个或多个 xml 配置文件,适用于 web 应用的 xml 配置方式
  • AnnotationConfigApplicationContext:从 Java 注解的配置类中 Spring 的 ApplicationContext 容器。使用注解避免使用 application. Xml 进行配置。相比 XML 配置,更加便捷。
  • AnnotationConfigWebApplicationContext:专门为 web 应用准备的用于读取注解创建容器的类。

1 依赖注入的三种方式

构造器注入;setter 方法注入;接口注入。