「spring源码」spring中bean的创建流程,三级缓存循环依赖问题
Posted 守夜人爱吃兔子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「spring源码」spring中bean的创建流程,三级缓存循环依赖问题相关的知识,希望对你有一定的参考价值。
概述
上面的图大概画了整个bean的创建流程,spring在创建bean之前首先从缓存池中去getBean,当从缓存池中获取不到bean时,才会去创建bean。了解spring源码的同学知道,spring中有三级缓存:
- 一级缓存:singletonObjects —> 存放完整bean的单例池
- 二级缓存:earlySingletonObjects —> 存放非完整bean的单例池,存放早期暴露的bean
- 三级缓存:singletonFactories —> 存放可以创建bean的factory,这里的factory主要是去创建bean的
代理对象
在创建bean的过程中,会通过bean后置处理去拿到bean的构造方法,对bean进行创建,然后对bean进行包装代理,如果bean不需要进行包装代理,就会返回一个早期暴露的对象,并放入二级缓存中,如果需要进行包装代理,就会返回一个factory,并放入三级缓存中。
然后会组装bean,这个过程就是去装配bean的属性,当bean装配完成后,bean就算是一个完整的bean了,最后会把这个完整的bean放入单例池中,即一级缓存中。
这就是bean创建的整个过程,这里概述了一下,也是想读到这篇文章的同学心里有一个大概,后面不至于晕乎乎的,下面就进入源码阅读阶段吧。
2.从缓存池中getBean
我们直接看doGetBean()这个方法,方法路径:AbstractBeanFactory#doGetBean(),在该方法最开始的时候就是去从单例池获取bean:
接下来就点进这个方法看看是怎么从缓存中获取bean的:
可以看到代码中,首先是从一级缓存中去拿bean,当拿不到bean时,且当前bean正在被创建,就会从二级缓存中去获取,当二级拿不到时,从三级缓存中获取factory对象,如果拿到了factory对象,会从factory中去获取bean,最后将该bean放入二级缓存中。
由于spring工程在启动时,缓存里肯定没有bean对象,于是就需要去创建bean,接下来看bean是如何创建的。
3.bean的创建
还是doGetBean()方法,看一下最重要的代码片段:
这里有两个方法,先看方法1,即createBean(beanName, mbd, args);点进这个方法去看看,这里省略了一些不重要的代码:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//... ...
// Prepare method overrides.
try {
/**
* 处理lookup-Method 和replace-Method,统称为overrides
*/
mbdToUse.prepareMethodOverrides();
}
//... ...
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
/**
* 里面有个后置处理器,如果这个后置处理器能返回一个bean,就直接返回了,不再进行后面的装配,不理会里面的依赖
* InstantiationAwareBeanPostProcessor
*/
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
//... ...
try {
/**
* 创建对象
*/
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
//... ...
}
可以看到这段代码片段里,首先是去处理overrides,然后去调用一个bean后置处理**(
InstantiationAwareBeanPostProcessor)**的方法,如果该方法返回了一个对象,则返回该对象,不进行后面的bean创建流程。
接下来看看创建对象这个方法:Object beanInstance = doCreateBean(beanName, mbdToUse, args);
在这个方法里,我们一部分一部分的来看,第一部分就是去创建bean对象:
来看看创建包装对象的部分,点进这个方法中:instanceWrapper = createBeanInstance(beanName, mbd, args); 依然省略一部分不重要的代码:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
//... ...
/**
* supplier的方式实例化对象
*/
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
/**
* 如果 FactoryMethodName 不为空,通过 instantiateUsingFactoryMethod() 去实例化对象
* 通过xml去指定 FactoryMethodName
* @Bean 当是static的时候,相当于是给对象配置了一个 FactoryMethodName
* 不是static的时候, 是uniqueFactoryMethodName
* 具体看ConfigurationClassPostProcessor
*/
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
/**
* 从Spring的原始注释中,可以知道这是一个Shortcut(快捷方式),什么意思呢?
* 当多次构建同一bean时,可以使用这个 Shortcut
* 也就是不需要再次推断使用哪种方式构造bean
*/
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
/**
* 由后置处理器(SmartInstantiationAwareBeanPostProcessor)决定 返回 有参数的 构造方法
* 如果只有一个无参构造方法,这里返回为 null
*/
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
/**
* 自动装配模型 != 自动装配技术
* 自动装配模型 默认为 0
*/
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
/**
* 通过默认的无参构造方法进行初始化
*/
return instantiateBean(beanName, mbd);
}
该方法主要是讲如何去创建bean,首先是通过supplier的方式实例化对象,其次是通过FactoryMethodName的方式去实例化对象(这种方式好像没用过,有大神知道吗),再然后是通过bean后置处理器去找到bean的构造方法,如果bean中定义了有参构造方法,就返回有参构造方法,
**determineConstructorsFromBeanPostProcessors(beanClass, beanName);**这里面有一些逻辑,感兴趣的同学可以自己去看一看,如果没有找到有参构造方法,就返回默认的无参构造方法。到这里就返回了一个BeanWrapper对象。
4.代理bean,放入三级缓存
前面bean已经被实例化出来了,因为spring中有些对象需要被代理,来看看这段代码:
在**getEarlyBeanReference(beanName, mbd, bean));**这个方法里,实际上就是去执行bean后置处理器中的方法,对bean进行代理:
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
最后会调用 ProxyFactory#getProxy 方法:
这里是不是就是我们熟悉的JDK代理和Cglib代理~
然后看 addSingletonFactory() 这个方法:
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);
}
}
}
这里就是将factory对象放入三级缓存中。
5.组装bean,循环依赖问题
这里就不贴代码了,感兴趣的同学下来再研究吧。这里就是对bean的属性进行填充,比如A依赖B,这里就是去填充B.
这里涉及到一个很经典的问题,就是spring中的循环依赖问题,spring到底是怎么解决循环依赖问题的呢?
我在读spring源码后,画了一张图,来清晰的表示:
假设A和B相互依赖,当创建A到了组装这一步骤时,需要去组装B,这个时候就会去getB,但是get不到,就会去创建B,当创建B到组装这一步骤时,需要去组装A,而A这个bean已经创建过了,并且在缓存中,可以得到A,这个时候B就组装完成了,而当B组装完成了,相应的A也组装完成。
最后A和B都是一个完整的bean,然后被放入一级缓存中,即单例池中。
最后
到这里bean的整个创建过程和spring的循环依赖问题就讲完了,希望看到这篇文章的同学有所收获。
我这里总结了【mysql笔记500多页资料集锦+1000道互联网大厂Java工程师面试题、spring、mybatis、jvm,Zookeeper,spring 点击免费领取】等资料,
有需要的朋友请点击上方免费获取。祝大家万事胜意!
以上是关于「spring源码」spring中bean的创建流程,三级缓存循环依赖问题的主要内容,如果未能解决你的问题,请参考以下文章