Spring Bean 的生命周期

文章目录

    • 一、前言
    • 二 、Bean 的生命周期
      • 2.1 Bean 的定义信息
      • 2.2 Bean 的创建
      • 2.3 Bean 的使用
      • 2.4 Bean 的销毁
      • 2.5 循环依赖
        • 2.5.1 什么是循环依赖
        • 2.5.2 Spring 解决循环依赖
          • 2.5.2.1 三级缓存
          • 2.5.2.2 提前暴露
        • 2.5.3 注意
          • 2.5.3.1 三级缓存结构中 map 分别存储什么类型对象
          • 2.5.3.2 三级缓存结构进行对象查找时,顺序是什么?
          • 2.5.3.3 如果只有一个 map 结构可以解决循环依赖问题吗?
          • 2.5.3.4 如果只有两个 map 结构 可以解决循环依赖吗?
          • 2.5.3.5 为什么使用三级缓存可以解决带 AOP 的循环依赖问题


一、前言

本文将图文并茂的为你讲述spring 中 bean 的生老病死!!!


二 、Bean 的生命周期

生命周期: 从对象的创建到使用到销毁的过程

Spring Bean 的生命周期_第1张图片

2.1 Bean 的定义信息

BeanFactory: Bean 工厂,是 Spring 的一个根接口

PostProcessor:后置处理器

  • BeanFactoryPostProcessor: 处理 Bean 的定义信息 BeanDifinition

  • BeanPostProcessor:修改 Bean 的信息

Spring Bean 的生命周期_第2张图片

Spring Bean 的生命周期_第3张图片

2.2 Bean 的创建

Aware 接口:

当容器中创建的 Bean 对象需要进行具体操作时,需要容器的其他对象,可以实现 Aware 接口,来进行操作,用于二次开发。

Spring Bean 的生命周期_第4张图片

2.3 Bean 的使用

在 Bean 创建时,属性赋值完成后,理论上 Bean 对象已经可以拿来使用了,但是Spring 考虑扩展性,会对 Bean 再做一些处理,完成初始化后即可调用

2.4 Bean 的销毁

Bean的销毁是在 Spring 容器关闭时进行销毁,及context.close()方法。

2.5 循环依赖

Spring 中的 Bean 默认情况下是单例的

2.5.1 什么是循环依赖

A 对象内包含了属性 b,B 对象内包含了属性 a。对 A 对象进行实例化后,对 b 属性赋值,这时候会引用 B 对象,判断 容器中是否有 B 对象,有则赋值,无则创建,B同理,这样程序就会报循环依赖的错误。

Spring Bean 的生命周期_第5张图片

2.5.2 Spring 解决循环依赖

解决方案:三级缓存、提前暴漏对象

当持有了某一个对象的引用之后,能否在后续步骤中给对象进行赋值操作?

可以,这样就可以解决循环依赖,其解决循环依赖的本质是 Bean 的实例化和初始化可以分开执行

Spring Bean 的生命周期_第6张图片

2.5.2.1 三级缓存

DefaultSingletonBeanRegistry 源码

	/**
	* 一级缓存
	* Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/**
	* 三级缓存
	* 用于保存 BeanName 和 创建 Bean 的工厂之间的联系
	* Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/**
    * 二级缓存
    * 保存 BeanName 和 创建 Bean 实例之间的联系,与 singletonFactories 的不同在于,当单例 Bean 被放到这里后,就可以通过 getBean 方法获取到,可以方便进行循环依赖的检测
    * Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

一级缓存 二级缓存 三级缓存
实例化对象类型 ConcurrentHashMap ConcurrentHashMap HashMap
容量 256 16 16
泛型 Object Object ObjectFactory

ObjectFactory:不是对象工厂,是函数式接口

@FunctionalInterface
public interface ObjectFactory<T> {

/**
    * Return an instance (possibly shared or independent)
    * of the object managed by this factory.
    * @return the resulting instance
    * @throws BeansException in case of creation errors
    */
   T getObject() throws BeansException;

}

jdk1.8后可以将 lambda 表达式作为参数放到方法的参数中,在方法执行的时候,并不会实际调用当前 lambda 表达式,只有在调用 getObject 方法的时候再回去调用 lambda 表达式

2.5.2.2 提前暴露

提前暴露指的是在 Bean 创建完成前 将对象暴露出去,Spring 在解决循环依赖问题时,在实例化后就将未创建完成的 Bean 的 ObjectFactory 放到 三级缓存,这样就把 Bean 提前暴露出去。

2.5.3 注意

2.5.3.1 三级缓存结构中 map 分别存储什么类型对象

一级缓存:成品对象

二级缓存:半成品对象

三级缓存:lambda 表达式

2.5.3.2 三级缓存结构进行对象查找时,顺序是什么?

一级缓存 ==> 二级缓存 ==> 三级缓存

2.5.3.3 如果只有一个 map 结构可以解决循环依赖问题吗?

理论上可行,实际没人这么干

使用两个 map 的意义在于 将成品对象和半成品对象进行区分,半成品对象不能暴露给外部使用,可以设置 标识位进行区分,但是操作比较麻烦,所以直接使用两个 map

2.5.3.4 如果只有两个 map 结构 可以解决循环依赖吗?

可以解决,但是前提是 没有使用代理对象的时候,当不使用 AOP 的时候, 两个 map 就可以解决循环依赖问题

2.5.3.5 为什么使用三级缓存可以解决带 AOP 的循环依赖问题
  • 一个容器中不能包含同名对象

  • 对象创建过程中,扩展阶段 原始对象有可能需要生成代理对象

  • 当代理对象出现时,要使用代理对象替换掉原始对象

  • 代理对象的创建是在初始化过程的扩展阶段,需要在前置处理过程的时候判断是否需要生成代理对象

  • 为什么使用 lambda 表达式(函数式接口)

    因为对象什么时候被暴露或被引用无法提前预知,所以只有在被调用的时候才可进行原始对象还是代理对象的判断,使用 lambda 表达式延迟执行的优点,类似于一种回调机制,不暴露的时候不执行,当需要被调用的时候才真正执行 lambda 表达式,来判断返回的是代理对象还是原始对象

你可能感兴趣的:(Spring,spring,java,spring,boot)