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
同时实现这三种方式时,调用顺序如下:
@PostConstruct
InitializingBean#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. ...
随机推荐
- Java实现哥德巴赫猜想
验证哥德巴赫猜想:任何一个大于 6 的偶数,都能分解成两个质数的和.要求输入一个整数,输出这个 数能被分解成哪两个质数的和. eg : 14 14=3+11 14=7+7 public class T ...
- 纯正中文版本pi-star系统下载
基于3.4.17修改(稳定,发热量少) 不支持RPI 4 a/b+ 完美支持树莓派0,1,2,3 默认刷好卡,启动已经设置好所有参数(选好TFT屏幕,调制解调器类型GPIO,打开了DMR服务器(460 ...
- struts 通配符的使用
使用通配符可以将配置量降到最低,十分方便 新建一个javaweb项目 在项目中加入Struts.xml( 选中项目右键MyEclipse-->project facets-->Struts ...
- 如何在Linux上安装Redis(内附详细教程)
前言 hello,好久不见,又断更了一段时间.同事大部分离职了,但是活还是一样,所以只能硬着头皮顶上.现在总算歇会了,决定开启Redis源码系列,希望不要啪啪啪打脸. 什么是redis? Redi ...
- CSS布局之Flex布局
Flex布局,可以简便.完整.响应式地实现各种页面布局. 浏览器支持:得到所有浏览器的支持.(注:Flex布局将成为未来布局的首选方案) 一. Flex布局的概念 Flex是Flexible Bo ...
- Linux dd工具磁盘读写测试分析
话说,Linux 自带的dd工具测试硬盘读写速度只能提供一个大概的测试结果,而且是连续IO 而不是随机IO ,理论上文件规模越大,测试结果越准确.理论上bs越大,所测得性能越高. 上句来自网上,是不是 ...
- Java学习之IO流及网络编程
一.字节 1.1字节输入流(java.io.InputStream) 此抽象类是表示字节输入流的所有类的超类 1.1.1定义了所有子类共性的方法: int read() 从输入流中读取数据的下 ...
- Python流程控制语句详解
1.程序结构 计算机在解决问题时,分别是顺序执行所有语句.选择执行部分语句.循环执行部分语句,分别是:顺序结构.选择结构.循环结构.如下图: 2.选择语句 2.1最简单的if语句 Python使用保留 ...
- 小球(总结sort和cmp函数、结构体排序)
问题 N: 小球(点击) 时间限制: 1 Sec 内存限制: 128 MB ...
- Mysql:bit类型的查询与插入
原文链接:https://www.cnblogs.com/cuizhf/archive/2013/05/17/3083988.html Mysql关于bit类型的用法: 官方的资料如下: 9.1.5. ...