Spring IoC


Spring IoC

概念梳理

什么是 IoC 和 DI ?

IoC :控制反转(Inversion of Control),这不是什么技术,而是一种设计思想。在 Java 开发中,IoC 意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

  • 谁控制谁,控制什么:传统 Java SE 程序设计,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象;而 IoC 是有专门一个容器来创建这些对象,即由 IoC 容器来控制对象的创建;谁控制谁?当然是 IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

DI :依赖注入(Dependency Injection),组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。IoC 和 DI 其实是同一个概念的不同角度描述。

传统获取对象的方式与 IoC 方式对比:

Bean 的创建

IoC 容器的初始化

Spring IOC 容器初始化的核心过程可以简单的如下图表示,主要有四个步骤(还有一些如:后置加载器,国际化,事件广播器等一些过程不展开):

  1. Bean 定义的定位,Bean 可能定义在 XML 中,或者一个注解,或者其他形式。这些都被用 Resource 来定位,读取 Resource 获取 BeanDefinition 注册到 Bean 定义注册表中。
  2. 第一次向容器 getBean 操作会触发 Bean 的创建过程,实例化一个 Bean 时,根据 BeanDefinition 中类信息等实例化 Bean 。
  3. 将实例化的 Bean 放到单例 Bean 缓存内。
  4. 此后再次向容器 getBean 就会从缓存中获取。

IoC 容器的初始化

扩展阅读:Spring IOC容器初始化

Bean 的注入方式

常用的注入方式

Bean 的注入方式是老生常谈的话题了,这里不展开细说。

目前主要有五种注入方式:SET 注入,构造器注入,静态工厂,实例工厂,注解方式

扩展阅读:Spring Bean注入

常用的 Bean 的配置参数

属性 含义 说明
id bean 的唯一标识
class 类的完全限定名
parent 父类 bean 定义的名字 如果没有任何声明,会使用父 bean ,但是也可以重写父类。重写父类时,子 bean 必须与父 bean 兼容,也就是说,接受父类的属性值和构造器声明的值。子 bean 会继承父 bean 的构造器声明的值,属性值,并且重写父 bean 的方法。如果 init 方法、destroy 方法已声明,他们会覆盖父 bean 相应的设置。保留的设置会直接从子 bean 获取,包括 depends on,auto wire mode ,scope ,lazy init 。
abstract 声明 bean 是否是抽象的 该属性决定该类是否会实例化。默认是 false 。注意:abstract 属性不会被子 bean 继承,所以,abstract 为 true 时需要对每个 bean 显示声明。
lazy-init 决定是否延迟实例化 如果为 false ,则启动时会立即实例化单例模式的 bean 。默认是 false 。注意:lazy-init 属性不会被子 bean 继承。
autowire 决定是否自动装配 bean 的属性 autowire 有 4 种模式(该属性不会被子 bean 继承。):
1.”no”:Spring 默认的模式。bean 的引用必须在 XML 文件中通过元素或 ref 属性显示定义。
2.”byName”:通过属性名使用自动装配。如果一个 Cat 类拥有一个 dog 属性,那么 Spring 会根据名字 dog 去寻找 bean ,如果没有找到 bean ,则不会自动装配。
3.”byType”:如果 Spring 容器只有该属性类型的一个 bean ,会自动装配。当有多个该属性类型的 bean 时会报错。如果没有,则不会自动装配。
4.”constructor”:针对构造器引用,和 “byType” 类似。参考:Spring中的自动装配
depends-on 该 bean 初始化时依赖的其他 bean bean 工厂确保其他 bean 在该 bean 之前完成初始化。注意:依赖项一般通过bean 属性或构造器声明,这个属性对其他依赖(如静态类或启动阶段数据库的准备)是必要的。注意:depends-on 属性不会被子bean继承。
scope bean 的作用域 singleton:单例模式。
prototype:默认选项,非单例模式。
request:对于 web 应用,每一个请求产生一个新的实例。
session:对于 web 应用,一个 session 产生一个实例。参考:Spring Bean的scope
init-method 初始化方法 bean 创建时的初始化方法。
destroy-method 销毁方法 bean 销毁时调用的方法,仅仅在 singleton 模式下起作用。

常用的注解

扩展阅读:Spring常用注解

Bean 解析注册过程

这个过程完成 Bean 的从配置文件到解析注册成 Bean 工厂的过程(对应代码在 AbstractApplicationContext.obtainFreshBeanFactory )。

Bean 解析注册过程

  1. 通过读取 XML 配置文件获取 Resource 资源,获取这个资源包含了路径 config/spring/local/appcontext-client.xml 文件下定义的 BeanDefinition 信息。
  2. 创建一个 BeanFactory ,这里使用 DefaultListableBeanFactory 。
  3. 创建一个载入 BeanDefinition 的解读器,这里使用 XmlBeanDefinitionReader 来载入 XML 文件形式 BeanDefinition ,通过一个回调配置给 factory 。
  4. 从定义好的资源位置读入配置信息,具体的解析过程由 XmlBeanDefinitionReader 的 loadBeanDefinitions() 方法来完成。完成整个载入和注册 Bean 定义之后,需要的 IoC 容器就建立起来可以直接使用了。

Bean 创建过程

这个过程完成 Bean 的实例化,如果是 singleton 类型的 Bean 还会放入缓存池中。(对应源码: AbstractApplicationContext.finishBeanFactoryInitialization )。

Bean 创建过程

扩展阅读:Spring源码深度解析

Bean 的生命周期

Bean 生命周期是 IoC 中非常核心的内容,Spring 为我们预留了很多的接口,让我们在 Bean 实例化前后、初始化前后可以写入一些自己的逻辑。

Bean 生命周期


Spring Bean 的生命周期


  1. 根据 BeanDefinition 信息,实例化对象,Constructor 构造方法;

  2. 根据 BeanDefinition 信息,配置 Bean 的所有属性(将 Bean 的引用注入到 Bean 对应的属性,Spring 可能存在循环依赖问题);

  3. 如果 Bean 实现了 BeanNameAware 接口,工厂调用 Bean 的 setBeanName ,参数为 Bean 的 Id ;

  4. 如果 Bean 实现 BeanFactoryAware 接口,Spring 将调用 setBeanFactory() 方法,将 BeanFactory 容器实例传入;

  5. 如果 Bean 实现 ApplicationContextAware 接口,Spring 将调用 setApplicationContext() 方法,将 Bean 所在的应用上下文的引用传入进来;

  6. 如果存在类实现了 BeanPostProcessor 接口,执行这些实现类的 postProcessBeforeInitialization() 方法,这相当于在 Bean 初始化之前插入逻辑 ;

  7. 如果 Bean 实现 InitializingBean 接口, 执行 afterPropertiesSet() 方法;

  8. 如果 Bean 指定了 init-method 方法,就会调用该方法。例:

    <bean init-method="init"> </bean>
  9. 如果存在类实现了 BeanPostProcessor 接口,执行这些实现类的 postProcessAfterInitialization() 方法,这相当于在 Bean 初始化之后插入逻辑;

  10. 这个阶段 Bean 已经可以使用了,scope为 singleton 的 Bean 会被缓存在 IoC 容器中;

  11. 如果 Bean 实现了 DisposableBean 接口, 在容器销毁的时候执行 destroy() 方法。

  12. 如果配置了destory-method 方法,就调用该方法。例:

    <bean destroy-method="customerDestroy"> </bean>

Bean 实例化顺序

Bean 实例化顺序

1.解析内部类,查看内部类是否应该被定义成一个 Bean ,如果是,递归解析。

2.解析 @PropertySource ,也就是解析被引入的 Properties 文件。

3.解析配置类上是否有 @ComponentScan 注解,如果有则执行扫描动作,通过扫描得到的 Bean Class 会被立即解析成 BeanDefinition ,添加进 beanDefinitionNames 属性中。之后查看扫描到的 Bean Class 是否是一个配置类(大部分情况是,因为标识 @Component 注解),如果是则递归解析这个 Bean Class 。

4.解析 @Import 引入的类,如果这个类是一个配置类,则递归解析。

5.解析 @Bean标识 的方法,此种形式定义的 Bean Class 不会被递归解析。

6.解析父类上的 @ComponentScan ,@Import ,@Bean ,父类不会被再次实例化,因为其子类能够做父类的工作,不需要额外的 Bean 了。

扩展阅读:Spring循环依赖问题

Spring 扩展接口

1.InitializingBean 接口

InitializingBean 接口中只有一个 afterPropertiesSet 方法,从方法的名称上很容易理解,这个方法是在 Bean 的属性都设置值后被调用,用于完成一些初始化工作。当然,在 Spring 的配置文件中 init-method 的配置也是在 Bean 的属性都设置值后被调用,用于完成一些初始化工作,不过在执行顺序上,接口的方法先于配置。值得注意的是,这两种方式都是用于完成一些初始化工作,所以相应的方法中不要编写一些复杂且执行时间很长的逻辑。

2.DisposableBean 接口

DisposableBean 接口中只有一个 destroy 方法,该方法会在 Bean 被销毁、生命周期结束之前被调用,用于做一些销毁的收尾工作。同样,在 Spring 的配置文件中 destroy-method 配置也完成同样的工作,不过在执行顺序上,接口的方法先于配置。

3.ApplicationContextAware 接口

ApplicationContextAware 中只有一个 setApplicationContext 方法。实现了 ApplicationContextAware 接口的类,可以在该 Bean 被加载的过程中获取 Spring 的应用上下文 ApplicationContext ,通过 ApplicationContext 可以获取 Spring 容器内的很多信息。

4.BeanFactoryAware 接口

BeanFactoryAware 接口中只有一个 setBeanFactory 方法。实现了 BeanFactoryAware 接口的类,可以在该 Bean 被加载的过程中获取加载该 Bean 的 BeanFactory ,同时也可以获取这个 BeanFactory 中加载的其它 Bean 。

5.FactoryBean 接口

FactoryBean 接口可以实现 Bean 实例化的个性定制,让 Spring 容器加载我们想要的 Bean 。实现了 FactoryBean 接口的类,可以通过实现 getObject 方法,实现加载我们想要的 Bean 。

6.BeanPostProcessor 接口

BeanPostProcessor 接口中有两个方法,分别为 postProcessBeforeInitialization 和 postProcessAfterInitialization 。实现了 BeanPostProcessor 接口的类,会在每个 Bean 初始化(即调用 setter )之前和之后,分别调用这个类中的 postProcessBeforeInitialization 方法和 postProcessAfterInitialization 方法,实现初始化的逻辑控制。

7.InstantiationAwareBeanPostProcessor 接口

InstantiationAwareBeanPostProcessor 接口中,常用的方法是 postProcessBeforeInstantiation 和 postProcessAfterInstantiation 。每个 Bean 的实例化(即调用构造函数)之前和之后,会分别调用实现了该接口的类中的 postProcessBeforeInstantiation 和 postProcessAfterInstantiation 方法。

8.BeanFactoryPostProcessor 接口

BeanFactoryPostProcessor 接口中只有 postProcessBeanFactory 方法。实现了该接口的类,可以在 Bean 被创建之前,获取容器中 Bean 的定义信息,并且可以进行修改。实现类中的 postProcessBeanFactory 方法只会被执行一次,且先于 BeanPostProcessor 接口的方法。

Aware 接口:

  • LoadTimeWeaverAware :加载 Spring Bean 时织入第三方模块,如 AspectJ 。
  • BootstrapContextAware :资源适配器 BootstrapContext ,如 JCA 、CCI 。
  • ResourceLoaderAware :底层访问资源的加载器。
  • PortletConfigAware :PortletConfig 。
  • PortletContextAware :PortletContext 。
  • ServletConfigAware :ServletConfig 。
  • ServletContextAware :ServletContext 。
  • MessageSourceAware :国际化。
  • ApplicationEventPublisherAware :应用事件。
  • NotificationPublisherAware :JMX 通知。

Aware 接口特点:

  • 都是 Aware 接口的子接口,即都继承了。Aware 接口。
  • 父接口 Aware 中没有定义任何方法。
  • 接口内均定义了一个 set 方法,set 参数就是我们需要获取的对象。

参考


文章作者: wenjun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wenjun !
评论
 上一篇
Spring MVC Spring MVC
Spring MVC1 概念梳理1.1 MVC 框架 MVC 全名是 Model View Controller ,是 模型(model)-视图(view)-控制器(controller) 的缩写,一种软件设计典范。 用一种业务逻辑、数据、
2020-05-29
下一篇 
Spring AOP Spring AOP
Spring AOP基本概念什么是 AOP ? AOP 是 Aspect Oriented Programming(面向切面编程) 的简称,和 OOP(面向对象编程)一样是一种编程思想,是对 OOP 的一种补充。 AOP 旨在将横切关注点
2020-05-25
  目录