Spring 源码分析——BeanFactory
本篇文章讨论 Spring 源码中 BeanFactory 和 BeanDefinition 的设计实现,分析 Spring 如何创建单例 Bean 和如何使用三级缓存解决循环依赖。
对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/01-bean-factory
概括
-
BeanFactory 接口定义了 getBean()
方法,BeanFactory 定义了获取 Bean 的父层函数; -
SingletonBeanRegistry 接口定义了 getSingleton()
方法,主要负责管理单例 Bean;由对应的实现类 DefaultSingletonBeanRegistry 负责三级缓存的具体实现(存储 Bean 最终是通过三个 Map 对象); -
AbstractBeanFactory 是 BeanFactory 接口的实现类,定义了 createBean()
方法,AbstractBeanFactory 将getSingleton()
函数整合到getBean
函数中,定义了 Bean 是从 beanMap 容器中获取还是单独初始化 Bean 对象; -
AbstractAutowireCapableBeanFactory 类是 createBean
的具体实现,它才是 Bean 创建的核心;此对象中整合了 Bean 的创建、装配、初始化、销毁等操作 -
BeanDefinition 描述了一个 Bean 的元数据信息,包含 bean 的类型、属性值、依赖关系等;初始化一个 Bean 必须要拿到对应的 BeanDefinition 对象; -
BeanDefinitionRegistry 定义了管理 BeanDefinition 对象的接口;其中加载一个 Bean 有很多方式如:XML 文件、Java 代码、注解等方式,因此 BeanDefinitionRegistry 接口有很多具体的实现类,针对了不同的 BeanDefinition 加载方式;
面试:Bean 生命周期
-
实例化:根据 Java 代码或者 XML 配置等方式加载 BeanDefinition 对象,获取 Bean 元数据信息,然后通过动态代理技术创建 Bean 实例对象; -
属性赋值:根据 Bean 的配置信息给 Bean 实例设置属性值; -
Aware 接口回调:Aware 接口让 Spring 有感知能力,可以让外部类拿到 Spring 上下文对象,并对实例化后的 Bean 进行自定义操作; -
BeanPostProcessor 前置后置处理器:提供了接口可以在 Bean 实例化前后影响 Bean 实例化结果; -
初始化:针对上面获取的信息和函数对 Bean 进行具体的初始化操作; -
使用 Bean:Bean 初始化完成后可以被其他对象使用; -
销毁:Spring 容器关闭时会调用 destory()函数执行销毁操作;
面试:BeanFactory 和 FactoryBean 区别
BeanFactory 是 Spring 容器管理的接口
BeanFactory 接口定义了管理和维护所有 Bean 对象,它是 Bean 创建的核心,定义了getBean()
函数,包含如何获取 Bean 对象,以及和 Bean 的三级缓存紧密相关
FactoryBean 是一个特殊的 Bean
FactoryBean 是一个特殊的工厂类,实现 FactoryBean 接口可以实现自己管理 Bean 的实例化和初始化操作;在 Spring 容器启动时会加载这些实现了 FactoryBean 接口的工厂类。
面试:Bean 的三级缓存
Bean 三级缓存的实现定义在 DefaultSingletonBeanRegistry 类中,该类实现了 SingletonBeanRegistry 接口,描述了 Spring 如何获取一个单例 Bean getSingleton()
。源码如下:
Spring 存储 Bean 实际上是通过 Map 对象,因为 Bean 实例化本身是比较耗时的过程,于是 Spring 引入了三级缓存来优化 Bean 的创建过程,三级缓存对应了三个 Map 对象:singletonObjects、earlySingletonObjects、singletonFactories。
-
singletonObjects(一层):存放完全初始化后的 Bean 实例;当 Spring 获取 bean 时首先从该容器中查询是否已经被实例化; -
earlySingletonObjects(二层):存放尚未完成全部初始化的 Bean 实例;当 Bean 完成了属性赋值和 Aware 回调等操作后就放到此对象;需要等待后置处理器完成后续操作; -
singletonFactories(三层):存放 ObjectFactory 对象,这是解决循环依赖的关键,当 Spring 实例化对象后在属性赋值时发现存在循环依赖,Spring 会先将该 Bean 的代理对象放入 earlySingletonObjects 缓存;使用 singletonFactories 中的 ObjectFactory 接口创建 Bean,并将该状态下的 Bean 放入 singletonFactories 三层缓存中,提前暴露给 IOC(IOC 可以在该缓存中拿到 Bean 实例)
面试:Spring 如何解决循环依赖
循环依赖是指:在 A 对象中依赖了对象 B,在 B 对象中依赖了 A;在没有三级缓存的情况下,A 对象实例化后在进行属性赋值时发现依赖了对象 B,但是 B 还没有实例化,于是实例化 B,B 实例化后属性赋值时发现依赖 A,但是 A 并没有彻底完成初始化操作(即在 singletonObjects 一层缓存中没有 A),于是 A 和 B 的初始化就陷入循环中。
public class A {
@Autowired
private B b;
}
public class B {
@Autowired
private A a;
}
解决单例 Bean 循环依赖的分析:
-
创建对象 A,完成实例化操作(bean 生命周期第一步),在完成初始化属性赋值(bean 生命周期第二步)时,发现依赖了对象 B,此时 Spring 会先将 beanA 放到三级缓存 singletonFactories 中; -
接着去创建对象 B,同样先完成 B 的实例化操作,然后发现依赖对象 A,则开始从缓存 beanMap 中获取对象 A 的实例,这时候虽然 singletonObjects 和 earlySingletonObjects 拿不到对象 A,但是可以在三级 singletonFactories 拿到对象 A(提前暴露),因此 B 可以继续完成初始化操作; -
最后 A 继续完成初始化操作,此时 B 已经完成了初始化,A 在 singletonObjects 缓存中可以直接拿到 B 实例,也就完成了 A 对象的初始化。
源码分析
从上面我们知道 BeanFactory 的getBean()
具体实现是AbstractBeanFactory
,而创建 Bean 对象的核心是AbstractAutowireCapableBeanFactory
类的createBean()
函数。
看 AbstractAutowireCapableBeanFactory 类的createBean()
函数源码:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
....
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
....
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
....
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
....
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
....
}
从上面看到在createBeanInstance()
之后,会立刻调用addSingletonFactory()
方法保存 ObjectFactory 对象,此方法定义在 DefaultSingletonBeanRegistry 类中,看对应源码:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized(this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
从上面可以看到,在createBeanInstance()
之后,Spring 会将 ObjectFactory 对象放入到singletonFactories
三层缓存中。
那么到此为止,已经看到 Bean 实例化后会提前暴露到第三层缓存中,也就是提前暴露给 IOC 容器,如此就解决了循环依赖问题。
为什么不能解决多例 bean 的循环依赖
因为单例的实现是依靠 beanMap 缓存,Spring 在创建对象时会优先从缓存中获取而不是创建新的对象;但是多例 bean 会每次都 getBean 创建新的对象,并不会写入到缓存中,Spring 也就无法解决此状态下的循环依赖问题。
Spring 源码专栏
此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题。
配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs
联系我
-
个人博客:http://tycoding.cn/ -
GitHub:https://github.com/tycoding -
微信公众号:程序员涂陌 -
QQ 交流群:866685601
原文始发于微信公众号(程序员涂陌):Spring源码分析—BeanFactory
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145636.html