Spring 01: Spring源码doGetBean中getSingleton是怎么调用的?
Spring源码doGetBean中getSingleton是怎么调用的?
单例对象的创建过程,不考虑循环依赖。
CheckList
doGetBean概览
学习spring源码的方法,专注于一条逻辑链,否则很容易迷失在不同的逻辑中。
考虑getBean中bean为单例的一般情况,学习时忽略其他情况,比如FactoryBean, BeanPostProcess的调用链,各种Aware接口的执行。
// 只是简单地梳理逻辑,为方便理解,一些代码进行了删除
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
// getSingleton方法有重载,这里只是查询了三级缓存。
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null)
// getObejctForBeanInstance 如果是工厂类的情况,返回getObject()方法返回的对象。ps: 单例对象实际存放在factoryBeanObjectCache里。
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 查询父接口
// 略
// 获取mdb,简单理解就是beanDefinition,包含所有的bean信息(beanDefinition可以继承)
// 源码中常见的变量mdb
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// getDependsOn
// 略
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// 原型模式处理
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
// scope情况处理
// 略
}
catch (BeansException ex) {
// 略
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
finally {
beanCreation.end();
}
}
// 类型转换
return adaptBeanInstance(name, beanInstance, requiredType);
}
单例对象调用链
简单回顾一下getBean的调用链
refresh()
finishBeanFactoryInitialization(beanFactory);
beanFactory.preInstantiateSingletons();
getBean(beanName);
doGetBean();
getSingleton(String beanName, ObjectFactory<?> singletonFactory)
singletonFactory.getObject(), 即createBean()
doCreateBean(beanName, mbd, args)
getSingleton(beanName, singletonFactory) 传参逻辑
注意到创建单例的难点在于理解getSingleton传入单例工厂,可以实现单例实例化的before、after处理,即 getSingleton(beanName, singletonFactory) 的实现逻辑。
这里所说的单例工厂仅仅指的是工厂方法,即通过调用此方法可以获取对象。而和工厂模式没有任何关系。
Java8 的 lambda 表达式提供工厂方法的精简实现,源码中 () -> {
try {
return createBean(beanName, mbd, args);
}
即为singletonFactory的lambda表达式实现。lambda表达式实现相比于匿名类有一个好处,即编译器可以自动捕获所需外部环境,形成闭包。此处createBean的方法即 this.createBean(),那么this指代的时哪个对象呢?
在lambda表达式中this指代的是外部的调用方,即调用doGetBean()的对象,DefaultListableBeanFactory 实例对象。
由此可以推断DefaultListableBeanFactory必然实现了createBean方法。(各个方法与其实现类对应关系详见之后的分析)
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
// 略去异常处理
}
综合所述,此lambda表达式一共捕获了几个变量?
答案是4个,分别是this(DefaultListableBeanFactory), beanName, mbd, args。
在Java7中,要求捕获的变量为final类型,所以在之前版本的spring中,createBean的签名如下:
protected abstract Object createBean(final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] args)
throws BeanCreationException;
而在Java8中,只要捕获变量是等效 final 的即可,所以我们看到的最新版Spring中createBean中没有final 关键字。
相反,如果使用匿名类实现,则需要手动传入这四个参数。从这点可以看出使用lambda表达式的精妙之处。
等效的匿名内部类表示如下:
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return DefaultListableBeanFactory.this.createBean(beanName, mbd, args);
}
});
createBean的实现逻辑
在Spring中,不带do的方法实际上只做方法的统筹调用,真正的具体实现是在do开头的方法里。
实际上真正对象的创建只需要beanName, mbd, args三个参数。
- methodOverride
- 提前返回代理类对象
- doCreateBean
调用链中方法与类的对应关系
refresh()
finishBeanFactoryInitialization(beanFactory);
beanFactory.preInstantiateSingletons();
getBean(beanName);
doGetBean();
getSingleton(String beanName, ObjectFactory<?> singletonFactory)
singletonFactory.getObject(), 即createBean()
doCreateBean(beanName, mbd, args)
AbstractApplicationContext::refresh
容器(ac)应用模板方法设计模式,模板方法refresh,属性包含了BFPP, BPP, 事件相关对象等。
ConfigurableApplicationContext
接口中定义了refresh方法,定义了生命周期控制,增加BFPP, BPP。
AbstractApplicationContext::finishBeanFactoryInitialization
完成bean工厂的初始化,即实例化+初始化大多数bean。
beanFactory.preInstantiateSingletons();
实例化其他的单例。ConfigurableListableBeanFactory定义,DefaultListableBeanFactory实现,大多数ac使用的bean工厂就是DefaultListableBeanFactory。
ConfigurableListableBeanFactory接口定义了有两个重要的方法,一个是preInstantiateSingletons
, 另一个是getBeanDefinition。
getBean(beanName);
doGetBean
BeanFactory定义了getBean(),AbstractBeanFactory实现了此方法。除此之外,AbstractBeanFactory还实现了doGetBean方法,使用模板方法模式,其使用很多其他接口,后续我会写一篇文章详细分析。
getSingleton
单例支持单独抽象成接口SingletonBeanRegistry,默认实现类DefaultSingletonBeanRegistry, 提供register方法,getSingleton重载
getSingleton(String beanName, boolean allowEarlyReference)// 从缓存获取
getSingleton(String beanName, ObjectFactory<?> singletonFactory)// 若缓存没有,则创建单例对象
createBean
doCreateBean
此方法在AbstractBeanFactory中定义为抽象方法,AbstractAutowireCapableBeanFactory
实现了createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)和doCreateBean()
AbstractAutowireCapableBeanFactory实现了属性自动注入方法,支持byName byType
方式。到此为止,真正可以创建对象和实现依赖注入的接口和其抽象实现类终于进入我们的视野,AbstractAutowireCapableBeanFactory
继承了AbstractBeanFactory,拓展了autowire的能力,同时AbstractBeanFactory具有ConfigurableBeanFactory
接口可配置的能力。最终,DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory
,成为了大多数ApplicationContext容器使用的对象。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?