Spring IoC bean 的初始化
前言
本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。
本篇文章主要介绍 Spring IoC 容器中 bean 的初始化阶段。
正文
我们在Spring IoC bean 的创建一文中分析创建 bean 实例的主要流程,此时创建出来的 bean 还是个属性未赋值的实例,在创建完之后会进入 populateBean() 方法,即进入属性赋值阶段。我们简单回顾一下,上次分析过的 doCreateBean() 方法:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
// 实例化 bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 如果bean的作用域是singleton,则需要移除未完成的FactoryBean实例的缓存
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 通过构造函数反射创建bean的实例,但是属性并未赋值,见下文详解
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 获取bean的实例
final Object bean = instanceWrapper.getWrappedInstance();
// 获取bean的类型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// BeanDefinition 合并后的回调,见下文详解
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
// 省略异常处理...
mbd.postProcessed = true;
}
}
// bean的作用域是单例 && 允许循环引用 && 当前bean正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
// 如果允许bean提前曝光
if (earlySingletonExposure) {
// 将beanName和ObjectFactory形成的key-value对放入singletonFactories缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 给 bean 的属性赋值
populateBean(beanName, mbd, instanceWrapper);
// 初始化 bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// 省略部分代码
}
上篇文章分析了 populateBean() 方法,这次我们总店分析 initializeBean() 方法。
bean 的初始化
AbstractAutoCapableBeanFactory#initializeBean
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// BeanAware的接口回调,见下文详解
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor的postProcessBeforeInitialization()回调,也就是bean初始化前的回调
// 在 ApplicationContextAwareProcessor实现的postProcessBeforeInitialization方法中会执行
// ApplicationContext Aware的接口回调。
// InitDestoryAnnotationBeanPostProcessor的postProcessBeforeInitialization()中会执行
// 标注了@PostConstruct注解的方法。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 调用bean的自定义初始化方法,如afterPropertiesSet,XML中的init属性指定的方法等
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor的postProcessAfterInitialization()回调,也就是bean初始化后的回调
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Aware 接口回调
AbstractAutowireCapableBeanFactory#invokeAwareMethods
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
// BeanNameAware接口方法回调
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// BeanClassLoaderAware接口方法回调
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
// BeanFactoryAware接口方法回调
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware)bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
通过实现这些 Aware 接口的 bean 的被初始化之前,可以取得一些相对应的资源,比如 beanName、beanFactory 等。
bean 的初始化前回调
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
// 遍历所有注册的BeanPostProcessor实现类,调用postProcessBeforeInitialization方法
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 在bean初始化方法执行前,调用postProcessBeforeInitialization方法
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
上面方法主要是调用了 BeanPostProcessor 的 postProcessBeforeInitialization() 方法,下面我们看一下 BeanPostProcessor 接口的定义:
public interface BeanPostProcessor {
/**
* bean初始化前调用,此时bean已经实例化并且属性已经赋值,Aware接口已经回调;返回非 {@code null} 会使用返回的bean
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* bean初始化后调用,返回非 {@code null} 会使用返回的bean
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
调用初始化方法
AbstractAutowireCapableBeanFactory#invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
// bean是否实现InitializingBean接口
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// 调用afterPropertiesSet方法
((InitializingBean) bean).afterPropertiesSet();
}
// 调用自定义的init方法,例如XML中init-method属性设置的方法
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
我们知道设置 bean 的初始化方法其实有三种方式 @PostConstruct、InitializingBean、自定义初始化方法,一个 bean 同时实现这三种方式时,调用顺序如下:
@PostConstructInitializingBean#afterPropertiesSet()- 自定义初始化方法
从上面方法可以很容易的看出 InitializingBean 接口的 afterPropertiesSet() 方法先于自定义初始化方法调用,那么 @PostConstruct 注解标注的方法在何时调用的呢?玄机就在上面介绍的 BeanPostProcessor 接口,InitDestroyAnnotationBeanPostProcessor 实现了该接口并重写了 postProcessBeforeInitialization() 方法调用了标注 @PostConstruct 注解的方法。我会在后续文章分析其实现。
bean 初始化后回调
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
// 遍历所有注册的BeanPostProcessor实现类,调用postProcessAfterInitialization方法
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 在bean初始化方法执行后,调用postProcessBeforeInitialization方法
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
总结
本篇文章主要分析了 Spring IoC bean 的初始化阶段流程,Spring 在此阶段也提供了2个扩展点;分别是 bean 的初始化前和初始化后,也就是 BeanPostProcessor 接口,该接口十分重要其它 processor 接口都是直接或间接在此接口上扩展出来的。
最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:https://github.com/leisurexi/tiny-spring。
Spring IoC bean 的初始化的更多相关文章
- Spring IoC容器的初始化过程
Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...
- Spring IoC bean 的创建(上)
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
- 小马哥讲Spring栈核心编程思想 Spring IoC+Bean+Framework
小马哥出手的Spring栈核心编程思想课程,可以说是非常专业和权威的Spring课程.课程主要的方向与核心是Spring Framework总览,带领同学们重新认识重新认识IoC,Spring IoC ...
- Spring IOC容器的初始化-(二)BeanDefinition的载入和解析
前言 1.在讲BeanDefinition的载入和解析之前,我们先来看看什么是BeanDefinition. Bean对象在Spring中是以BeanDefinition来描述的,也就是说在Sprin ...
- Spring IOC容器的初始化—(一)Resource定位
前言 上一篇博文“ Spring IOC是怎样启动的 ”中提到了refresh()方法,这个就是容器初始化的入口.容器初始化共有三个阶段: 第一阶段:Resource定位 第二阶段:BeanDefin ...
- Spring IoC bean 的加载
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
- Spring 源码 (1)Spring IOC Bean 创建的整体流程
Spring IOC 中涉及的重要接口 BeanDefinition Bean的描述信息,实现类包括 RootBeanDefinition 和 GenericBeanDefinition,Bean的描 ...
- Spring IOC bean加载过程
首先我们不要在学习Spring的开始产生畏难情绪.Spring没有臆想的那么高深,相反,它帮我们再项目开发中制定项目框架,简化项目开发.它的主要功能是将项目开发中繁琐的过程流程化,模式化,使用户仅在固 ...
- Spring IOC容器的初始化-(三)BeanDefinition的注册
---恢复内容开始--- 前言 在上一篇中有一处代码是BeanDefiniton注册的入口,我们回顾一下. 1.BeanDefiniton在IOC容器注册 首先我们回顾两点,1. 发起注册的地方:2. ...
随机推荐
- 第七届蓝桥杯JavaC组国(决)赛真题
解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.平方末尾 能够表示为某个整数的平方的数字称为"平方数" 比如,25,64 虽然无法立即说出某个数是平方数,但经常可 ...
- 第七届蓝桥杯JavaA组国(决)赛部分真题
解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.阶乘位数 阶乘位数 9的阶乘等于:362880 它的二进制表示为:1011000100110000000 这个数字共有19位. 请你计 ...
- 【Python源码剖析】对象模型概述
Python 是一门 面向对象 语言,实现了一个完整的面向对象体系,简洁而优雅. 与其他面向对象编程语言相比, Python 有自己独特的一面. 这让很多开发人员在学习 Python 时,多少有些无所 ...
- hibernate 用注解方式生成uuid方法
//配置uuid,本来jpa是不支持uuid的,但借用hibernate的方法可以实现. @GeneratedValue(generator = "uuid") @Generate ...
- [蓝桥杯]算法提高 GPA
问题描述 输入A,B两人的学分获取情况,输出两人GPA之差. 输入格式 输入的第一行包含一个整数n表示A的课程数,以下n行每行Si,Ci分别表示第i个课程的学分与A的表现. GPA=Σ(Si*Ci) ...
- qt程序添加文件版本号
1.需要一个 *.rc 文件,用以保存相关信息.比如添加一个 app.rc 里面内容如下所示: IDI_ICON1 ICON DISCARDABLE "app.ico" ----- ...
- MYSQL SQL 语句修改字段默认值
alter table tablename alter column drop default; (若本身存在默认值,则先删除) alter table tablename alter column ...
- netty实现消息中心(一)思路整理
一.需求 需要实现直播间的以下功能: 群发消息(文本.图片.推荐商品) 点对点私发消息(文本.图片.推荐商品) 单个用户禁言 全体用户禁言 撤回消息 聊天记录持久化 二.技术实现 服务端消息中心采用n ...
- ORM框架 Mybatis、Hibernate、Spring Data JPA之到底该用谁,谁更牛*
在持久层框架中无可厚非的就是mybatis了,但是也会经常被人问到为啥要用mybatis,为啥不用hibernate,jpa.很多人各级都是地铁爷爷看手机的表情,似乎从来没想过这个问题.“公司叫用我就 ...
- @codeforces - 575E@ Spectator Riots
目录 @description@ @solution@ @accepted code@ @details@ @description@ 一个以 (0, 0) 为左下角,(10^5, 10^5) 为右上 ...