深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇

目录

  • 前言
  • 一.Bean的生命周期
    • 1.1 Bean的实例化阶段
    • 1.2 Bean的初始化阶段(重点)
    • 1.3 Bean的完成阶段
  • 二.循环引用问题(面试常问题)
  • 三.Spring的三级缓存(重点来了)
  • 四.完整的Spring IoC整体总结

前言

本篇是接着bean的创建基本流程,后置处理器的的续写,了解本篇的Bean的生命周期需要熟悉bean创建的基本过程和后置处理器

Bean的创建基本流程:http://t.csdn.cn/9nfTE
后置处理器详解:http://t.csdn.cn/PjMYc

最后,文章部分为个人的总结与思考,如果遗漏欢迎指正或补充,感谢您的阅览,愿您终有所获


一.Bean的生命周期

先来简单回顾一下bean的实例化步骤

bean的实例化步骤总共5步

1.将bean的配置信息封装成一个BeanDefinition对象
2.把所有的BeanDefinition对象存储到beanDefinitionMap的Map集合中
3.Spring框架再对该Map进行遍历,取出每个BeanDefinition对象的配置信息,通过反射创建bean
4.创建好的Bean对象存储在一个名为singletonObjects(单例池,也在BeanFactory中维护)的Map集合中
5.当调用getBean方法最终从该Map集合中取出Bean实例对象返回


而bean的生命周期是在bean实例化后,即通过反射创建对象那时开始,到注入属性,成为一个完整的bean,最后存储到单例池中,这个过程称为bean的生命周期

bean的生命周期大致分为三个阶段:
1 Bean的实例化阶段
2 Bean的初始化阶段
3 Bean的完成阶段

下面就会对这3个阶段做详细分解


1.1 Bean的实例化阶段

Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton(单例)的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化


1.2 Bean的初始化阶段(重点)

Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,Spring的注解功能等

spring高频面试题Bean的循环引用问题都是在这个阶段体现的


Spring Bean的初始化过程涉及如下几个过程:

①Bean实例的属性填充(就是通过BeanDefinition中封装的bean的属性信息set到bean里)

在这里插入图片描述

下面是BeanDefinition中封装的bean的属性位置,上图看印象更深刻

深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇_第1张图片


Bean实例属性填充几种情况

Spring在进行属性注入时,会分为如下几种情况:

  • 注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
  • 注入单向对象(比如举例,service依赖dao的注入,而dao创建不依赖service的注入,简而言之,就是dao对象是service对象的属性)引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
  • 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,暂时别急,下面会详细阐述解决方案。
    深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇_第2张图片

②Aware接口属性注入(当有些bean实现一些扩展接口,需要什么对象,框架就通过Aware接口规定的方法注入进去,在这里不是重点)

③BeanPostProcessor的before()方法回调(bean的后处理器的重写方法)

④InitializingBean接口的初始化方法回调

⑤自定义初始化方法init回调

⑥BeanPostProcessor的after()方法回调(bean的后处理器的重写方法)


1.3 Bean的完成阶段

经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个生命周期。


二.循环引用问题(面试常问题)

循环依赖发生时机是在Bean实例属性填充时

多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用"

深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇_第3张图片
属性互相注入,导致无法创建完整的bean对象,上图更形象的展示了B依赖A注入,A依赖B注入的情况,结果形成闭环


循环依赖图解过程

深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇_第4张图片


三.Spring的三级缓存(重点来了)

Spring提供了三级缓存存储 完整Bean实例 和 半成品Bean实例 ,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

public class DefaultSingletonBeanRegistry ... {
	//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
	Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
	//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
	Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
	//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
	Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}

可以看看它在源码的位置

深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇_第5张图片


三级缓存详细图解原理

下面图解给的注释很详细,按步骤来,细看就会发现是如何用三级缓存解决循环依赖的问题的,看懂了就是醍醐灌顶

深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇_第6张图片


UserService和UserDao循环依赖的过程结合上述三级缓存描述一下

  • UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
  • UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
  • UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
  • UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
  • UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
  • UserService 注入UserDao;
  • UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存

当然,如果有兴趣去底层翻阅源码查看其中调用过程和实习方式可以根据黑马的调用流程图来翻阅,避免迷失在源码中,反正翻源码时务必目的明确,别贪。
三级缓存源码调用流程图:https://pan.baidu.com/s/1jwruaz9NmFN9sFHf6Ksrqw?pwd=sohp
提取码:sohp


四.完整的Spring IoC整体总结

深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇_第7张图片

你可能感兴趣的:(学习经验,#,Spring,spring,java,后端)