Spring Bean 的一生包括其从创建到消亡的整个过程:

实例创建 => 填充 => 初始化 => 使用 => 销毁。

这里需要注意的是,从 bean 实例的创建到可以使用之间还包括【填充】和【初始化】两个步骤。

AbstractAutowireCapableBeanFactory::createBean:bean 创建核心方法,包含创建、填充 bean 实例及应用 post-processors 等逻辑。

一、实例创建

1、实例化前置处理

InstantiationAwareBeanPostProcessor 为 BeanPostProcessor 子接口,用以提供【创建实例】前后回调处理。

如果有实现 InstantiationAwareBeanPostProcessor 接口,则应用此接口,返回结果如果不为 null,则直接返回作为 bean 实例。

2、doCreateBean

实际用于执行 bean 创建的方法,所有的创建、填充、初始化、注册销毁等逻辑都在此处处理。

BeanWrapper:Spring 底层 JavaBean 结构核心接口,提供了分析和管理 JavaBean 的相关操作。不直接使用,通常隐式的通过 BeanFactory 或者 DataBinder 来使用。此处执行逻辑即为使用 BeanWrapper 对象。

factoryBeanInstanceCache:存储 FactoryBean name --> BeanWrapper 键值映射。执行实例创建伊始,会先从 factoryBeanInstanceCache 查询获取,存在则直接获取(获取后删除)使用。

好吧,这里有个问题,为什么会有个 factoryBeanInstanceCache 缓存?

源头在于对单例 FactoryBean 类型操作,getSingletonFactoryBeanForTypeCheck。

创建 bean 实例 createBeanInstance:

优先级顺序:

  • 通过 InstanceSupplier 创建(5.0以后)

  • 通过工厂方法创建

  • 构造函数创建

至此,bean 实例已创建完毕。

此处还有一个 post-processor 处理:MergedBeanDefinitionPostProcessor,用于 bean 定义修改(只针对 RootBeanDefinition:merge 了多个来源 BeanDefinition 的运行时视图)。

3、单例实例提前暴露

为了解决单例循环依赖问题,提前将未完全创建好的单例实例缓存起来。

这里说的未完全创建好是指还不能正常使用。

earlySingletonExposure 条件:

  • 单例:scope 为 “singleton” 或者 ”“。

  • 允许自动处理循环依赖:allowCircularReferences 默认 true

  • 单例 bean 处于创建中:DefaultSingletonBeanRegistry:singletonsCurrentlyInCreation 存储所有处于创建中的 bean 名称。

addSingletonFactory:

将 singletonFactory 添加到 singletonFactories 缓存中,以备解决循环依赖使用。

singletonFactories 是什么呢?

字面意思为单例工厂缓存(bean name -> ObjectFactory ):即所谓的第三级缓存,存储目标 bean 所对应的 bean 工厂对象键值。

那 ObjectFactory 这个对象是怎么获取的呢?

SmartInstantiationAwareBeanPostProcessor::getEarlyBeanReference

SmartInstantiationAwareBeanPostProcessor 是 InstantiationAwareBeanPostProcessor 的扩展接口。

InstantiationAwareBeanPostProcessor 我们说过,是作用在创建实例前后。此处为创建实例后情景。

ObjectFactory 虽名为工厂,其实际为用以在 bean 创建早期,访问相应 bean 的一个引用。

什么是早期呢?

就是这会儿,刚创建完实例,还没有进行相应的填充、初始化等后续操作。

那为什么是暴露个引用,而不是直接给出目标对象呢?

因为目标 bean 可能还会经过其它 post-processors 处理。像 AbstractAutoProxyCreator::getEarlyBeanReference 中的代理逻辑处理。

二、填充

属性填充,作用于 AbstractAutowireCapableBeanFactory::populateBean。

1、属性填充前置处理

continueWithPropertyPopulation:是否继续处理属性填充判断。

这里的说明是在执行属性填充前给予任何 InstantiationAwareBeanPostProcessors 一个机会来变更 bean 的状态。

什么意思呢?

就是 InstantiationAwareBeanPostProcessors 的 postProcessAfterInstantiation 处理,对目标 bean 做相应的变更。

做什么变更呢?

这个节点在 Spring 自动注入操作之前,可以执行个性化的属性注入。同时,方法返回值会赋予 continueWithPropertyPopulation,以决定是否执行后续的逻辑。

这里有一个点需要注意:

如果当前 InstantiationAwareBeanPostProcessors::postProcessAfterInstantiation 返回 false,那么 bean 属性填充步骤则就此终止,不会再执行其它的 InstantiationAwareBeanPostProcessors 及后续的 Spring bean 属性填充过程。

2、属性填充

MutablePropertyValues

PropertyValues 接口的一个实现,提供对属性的各种操作,同时提供相应的构造函数来支持深度复制及基于 Map 的构造。

自动注入方式:按顺序 BY_NAME => BY_TYPE

BY_NAME

autowireByName 根据名称填充

填充什么呢?

unsatisfiedNonSimpleProperties。

什么是 unsatisfiedNonSimpleProperties 呢?

  • 可写的:即拥有写方法。

  • 需要依赖检查的:基于 ignoredDependencyTypes 属性设置判断。

  • 非本身类型的。

  • 非简单类型属性的:属性本身类型及数组元素类型为非简单类型。包括(基本类型及其包装类型,如 int、Integer 等)

注入:

首先根据属性名称判断 bean 存在:

即是否包含在 bean 工厂及外部注册单例 bean。

  • alias 的,会做相应的名称转换。

  • 存在继承关系的,会级联向上查询。

根据属性名称获取 bean:AbstractBeanFactory::getBean。

属性设置。

注册 bean 依赖:dependentBeanMap beanName -> Set<BeanName>,即记录 bean 及其依赖 bean 关系。

BY_TYPE

autowireByName 根据类型填充。

一个 BeanFactory 里必须恰好只有一个匹配需要类型。

同样,首先获取需要填充的属性:unsatisfiedNonSimpleProperties。

排除 Object 类型属性,填充没有意义。

处理依赖。

属性设置

注册 bean 依赖。

3、依赖检查

依赖检查分为两部分:一个基于 InstantiationAwareBeanPostProcessor::postProcessPropertyValues 处理。一个基于 AbstractBeanDefinition::dependencyCheck 处理。

InstantiationAwareBeanPostProcessor:

对特定的属性进行依赖检查及处理;对特定属性值进行替换,添加或者删除。

如 RequiredAnnotationBeanPostProcessor、 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、MockitoPostProcessor等。

dependencyCheck

检查所有暴露的属性是否都已赋值。

4、属性赋值

将上述处理过的属性值填充到 bean 实例。

三、初始化

应用工厂回调,定义的初始化方法及post-processors。

1、Aware 处理

Aware 代表了各种各样的资源,处理 Aware 即为将相应的资源添加到 bean 实例中。

如 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 等。

2、BeanPostProcessorsBeforeInitialization

顾名思义,这里的 BeanPostProcessors 是初始化之前的处理。

如 AbstractAdvisingBeanPostProcessor 检查。

3、执行初始化方法

a)实现了 InitializingBean 接口的 bean,执行相应的 afterPropertiesSet 方法。

b)定义了 initMethod 的,触发相应的方法调用。

两者是否可以同时存在呢?

可以,如果同时存在,但是初始化方法名称不能为 afterPropertiesSet。执行顺序为先 a 后 b。

4、BeanPostProcessorsAfterInitialization

同 2,此处为初始化之后的处理。

如 BeanValidationPostProcessor、ApplicationListenerDetector 等。

其实很多 PostProcessor 是既有 Before 处理逻辑,亦有 After 处理逻辑的,此处不再赘述。

四、disposable bean 注册

bean 工厂维护了一个 disposable bean 列表(bean name --> disposable instance)。在工厂关闭销毁时,同时销毁相应的 bean 实例对象。

定义销毁可以通过实现 DisposableBean 或者 AutoCloseable 接口或者自定义销毁方法。

如果使用一个定义了相应销毁方法的对象,又不想其执行销毁方法时怎么办呢?

注解或者配置其销毁方法为空,如:@Bean(destroyMethod = "")。

DestructionAwareBeanPostProcessor:实例销毁前,用户可以自定义执行特定的操作。如:ApplicationListenerDetector 移除相应的 Listener;ScheduledAnnotationBeanPostProcessor 移除定时任务等。

Spring Bean 的一生的更多相关文章

  1. Spring Bean的一生

    Spring Bean的一生 When you work directly in Java, you can do anything you like with your objects and do ...

  2. Spring 了解Bean的一生(生命周期)

    转载 https://blog.csdn.net/w_linux/article/details/80086950 该篇博客就来了解IoC容器下Bean的一生吧,也可以理解为bean的生命周期. ## ...

  3. Spring Bean生命周期,好像人的一生。。

    大家好,我是老三,上节我们手撸了一个简单的IOC容器五分钟,手撸一个Spring容器!,这节我们来看一看Spring中Bean的生命周期,我发现,和人的一生真的很像. 简单说说IoC和Bean IoC ...

  4. Spring8:一些常用的Spring Bean扩展接口

    前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...

  5. Spring Bean详细讲解

    什么是Bean? Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象. Spring 容器会自动完成@bean对象的实例化. 创建应用对象之间的协作关系的行为称为:装配( ...

  6. Spring Bean的生命周期(非常详细)

    Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...

  7. spring bean的生命周期

    掌握好spring bean的生命周期,对spring的扩展大有帮助.  spring bean的生命周期(推荐看)  spring bean的生命周期

  8. spring bean的重新加载

    架构体系 在谈spring bean的重新加载前,首先我们来看看spring ioc容器. spring ioc容器主要功能是完成对bean的创建.依赖注入和管理等功能,而这些功能的实现是有下面几个组 ...

  9. Spring Bean

    一.Spring的几大模块:Data access & Integration.Transcation.Instrumentation.Core Spring Container.Testin ...

  10. 【转】Spring bean处理——回调函数

    Spring bean处理——回调函数 Spring中定义了三个可以用来对Spring bean或生成bean的BeanFactory进行处理的接口,InitializingBean.BeanPost ...

随机推荐

  1. shell 定时清理一定时间内未使用的目录下文件脚本

    配合crontab即可实现标题 使用 bash + 脚本 + 目录 bash xx.sh /root/xx/ 脚本如下: #!/bin/bash # 将current转换为时间戳,精确到秒 CURRE ...

  2. 【LeetCode动态规划#09】完全背包问题实战,其二(零钱兑换和完全平方数--求物品放入个数)

    零钱兑换 力扣题目链接(opens new window) 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能 ...

  3. SpringBoot面试题的零碎整理

    面试题1:简述一下Springboot相对SSM做了哪些提升? 首先,SpringBoot是采用"约定大于配置"(Convention over Configuration)的理念 ...

  4. Spring Cloud Zuul 获取当前请求的路由信息和路由后端的服务节点信息

    基本思路 参考spring-cloud-zuul-ratelimit开源项目,在过滤器中根据当前的请求路径,判断当前的路由信息,当取得路由信息后,可以对服务的调用次数做统计等操作. 具体实现 创建一个 ...

  5. 最经典的TCP性能问题

    目录 问题描述 问题的原因 什么是delay ack 什么是Nagle算法 如果client启用Nagle,并且server端启用了delay ack会有什么后果呢? 再来看一个经典例子和数据分析 回 ...

  6. HashMap源码窥探

    目录 前言 HashMap的数据结构 HashMap的put方法 HashMap的get方法 HashMap的初始化 HashMap的containsKey方法 HashMap的putAll方法 Ha ...

  7. 游戏H5引擎Canvas屏幕自适应CSS代码

    canvas.style = `touch-action: none; width:${ width }px; height:${ height }px; cursor: inherit;`;

  8. Linux或者Mac解压乱码问题

    1.unar : 命令行解压工具 2.安装: ubuntu等Linux安装方法:sudo apt install unar mac系统安装方法:brew install unar 现在mac电脑用 T ...

  9. 【Azure 应用程序见解】通过无代码方式在App Service中启用Application Insights后,如何修改在Application Insights中显示的App Service实例名呢?

    问题描述 在App Service中,可以非常容易的启动Application Insights服务.默认情况中,在Application Insights中查看信息时候,其中的对象名称默认为App ...

  10. expect tcl 摘录

    目录 部分参考来源说明 例子 expect命令 核心命令有三个 spawn.expect.send 其他expect命令 expect命令的选项 变量 tcl摘录 数据类型 符号 命令 其他说明 部分 ...