目录
前言
大家好,我是oxye马儿,最近研究了Springboot的启动流程,非常长,bean加载流程在其中只是一块拼图,以至于我意识到并不能在一篇文章中将清楚,可能掌握很好以后可以概括一下吧
这篇先写一下Spring的三级缓存+循环依赖的解决方式
Springboot版本:2.3.2
Spring的三级缓存
寻找bean加载位置
下面我们找一下bean加载的位置
注意
找到的不是所有bean加载的位置,有一些bean在这一步之前就加载好了,比如系统、Spring需要先加载的bean,我们找的是其他大部分bean的加载位置(@Controller、@Service等注解的bean)
从启动类出发,中间会跳过一些方法或接口,见到方法点进去就完事儿了
类 | 方法 | 代码 |
---|---|---|
带注解@SpringBootApplication的启动类 | public static void main(String[] args) | SpringApplication.run(XxxApplication.class, args); |
org.springframework.boot.SpringApplication | public ConfigurableApplicationContext run(String… args) | refreshContext(context); |
org.springframework.context.support.AbstractApplicationContext | public void refresh() | finishBeanFactoryInitialization(beanFactory); |
org.springframework.context.support.AbstractApplicationContext | protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) | beanFactory.preInstantiateSingletons(); |
org.springframework.context.ConfigurableApplicationContext | public void preInstantiateSingletons() | getBean(beanName); |
org.springframework.beans.factory.support.AbstractBeanFactory | public Object getBean(String name) | return doGetBean(name, null, null, false); |
org.springframework.beans.factory.support.AbstractBeanFactory | protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) | ↓ |
走到doGetBean方法,下面就有很多逻辑都可以看,下面这个表格不是递进的顺序,是doGetBean方法中并列的一些操作,因为本主题只涉及单例bean,所以只看下面代码
代码 | 简介 |
---|---|
Object sharedInstance = getSingleton(beanName); | 从缓存取bean |
sharedInstance = getSingleton(beanName, () -> {return createBean(beanName, mbd, args);}) | 如果isSingleton(),创建单例bean |
三级缓存(和他们的朋友们)
三级缓存所属的类叫DefaultSingletonBeanRegistry
此类中的三级缓存:singletonObjects、earlySingletonObjects、singletonFactories,除了他们仨,还有一些辅助的缓存,例如singletonsCurrentlyInCreation,来记录正在创建的bean
/**
* Cache of singleton objects: bean name to bean instance.
* 单例对象的缓存:bean名称->bean实例
* 第一级缓存,存放实例化好的单例对象
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* Cache of early singleton objects: bean name to bean instance.
* 早期单例对象的缓存:bean名称->bean实例
* 第二级缓存,存放提前曝光的实例对象
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
* Cache of singleton factories: bean name to ObjectFactory.
* 单例工厂的缓存:bean名称->ObjectFactory
* 第三级缓存,存放要被实例化的对象的对象工厂
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* Names of beans that are currently in creation.
* 存放正在创建的bean
*/
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* Set of registered singletons, containing the bean names in registration order.
* 一组已注册的单例实例,bean名称按注册顺序排列
*/
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
bean创建与循环依赖解决流程
从缓存取bean
尝试从各级缓存取bean
先从一级缓存singletonObjects中获取
如果获取不到,并且对象正在创建中
再从二级缓存earlySingletonObjects中获取
如果还是获取不到就从三级缓存singletonFactories中取
如果三级缓存有,则移入二级缓存并返回,否则返回null,执行创建bean
/**
* 返回指定名称的 已注册的原始单例对象
* 检查已实例化单例对象,也允许当前创建的单例使用一个早期引用)(解决了循环依赖)
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//尝试从一级缓存singletonObjects中获取
Object singletonObject = this.singletonObjects.get(beanName);
//如果获取一级缓存singletonObjects拿不到,并且bean在创建中(缓存singletonsCurrentlyInCreation获取到了),再执行以下逻辑,否则说明又没创建好也没创建中,方法直接返回null
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//“给爷锁住!” 防止其他地方操作singletonObjects引起的并发安全问题
synchronized (this.singletonObjects) {
//尝试从二级缓存earlySingletonObjects中获取
singletonObject = this.earlySingletonObjects.get(beanName);
//二级也拿不到,尝试从三级缓存singletonFactories中获取,入参allowEarlyReference是true的
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//三级也拿不到,是真没有,可以返回个空了
if (singletonFactory != null) {
//如果三级拿到了,三级缓存移动到了二级缓存
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
创建bean
缓存中获取不到时,再走创建流程
走到
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
的
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
createBean
这段方法比较长,我™直接阉割成一行
/**
* 此类核心方法:创建bean实例
* 填充bean实例,应用后置处理器 等
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
//***
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//***
}
doCreateBean
将创建的bean添加到三级缓存
Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外提前暴露依赖的引用值(提前曝光),根据对象引用能定位到堆中的对象,其原理是基于Java的引用传递
/**
* 创建指定的bean。此时已经进行了预创建处理,例如检查{@code postProcessBeforeInstantiation}回调。
* 要区分默认bean实例化、使用工厂方法和自动连接构造函数的不同。
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// bean实例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 将原始bean或代理bean加入三级缓存,这一步在实例化之后,属性填充之前,是解决循环依赖的关键,
// 后续属性填充的时候,依赖这个bean a的bean b就可以直接获取三级缓存中的bean a
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// bean属性填充,此处会获取循环依赖的其他bean
// 此时会创建bean b,顺利填充
populateBean(beanName, mbd, instanceWrapper);
// bean初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
// 这一步是因为,在bean b创建的过程中,检测到a的时候,将a从三级移到二级了,所以要返回二级缓存中的
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
return exposedObject;
}
addSingletonFactory
三级缓存添加bean
/**
* 添加给定的singleton工厂以生成指定的singleton
*/
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);
}
}
}
获取创建的bean
完全初始化之后,将bean放入到一级缓存中供其他bean使用
/**
* 从二三级缓存移除,放入一级缓存
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
流程图
这篇讲得很好,直接用一下里面的图,侵删
假设现在有a b 两个bean互相依赖,下图是创建a的过程,粉色的getSingleton获取的是null,绿色的getSingleton会获取createBean创建完成的bean
整个创建a b Bean的流程(没使用aop的情况)
整个创建a b Bean的流程(没使用aop的情况)
另一种画法
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/93737.html