「Spring源码」循环依赖解析&遇到的bug分析
- 本篇分析内容
- Spring循环依赖源码解析
- @Async引发的bug分析
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'XXXXXA':
Bean with name 'XXXXXA' has been injected into other beans [XXXXXB] in its raw version as part of a circular reference,
but has eventually been wrapped. This means that said other beans do not use the final version of the bean.
This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
Spring循环依赖源码解析
@Component
public class A {
@Autowired
private B b;
public void sayHello(){
System.out.println("hello");
}
}@Service
public class B {
@Autowired
private C c;
@Async
public void sayHello(){
System.out.println("hello");
}
}@Service
public class C {
@Autowired
private D d;
public void sayHello(){
System.out.println("hello");
}
}@Component
public class D {
@Autowired
private A a;
public void sayHello(){
System.out.println("hello");
}
}@ComponentScan("org.springframework.test.self.annotation")
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(A.class);
applicationContext.register(B.class);
applicationContext.register(C.class);
applicationContext.register(D.class);
applicationContext.refresh();
A bean = (A) applicationContext.getBean("a");
bean.sayHello();
}
}
大致流程
文章图片
前置说明
首先我先申明下,真的不知道谁取得一/二/三级缓存这几个名字的,但是很恐怖的事大家都在用,就是几个
HashMap
整这么花里胡哨的,Spring里面缓存有很多,叫四级,五级,六级。。。。。。n+级?为了方便大家理解还是沿用了这几个名字了。我看了下基本就是这个方法里面用到的这三个缓存
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
,英文原版注释也放出来,各位怎么方便记怎么来//一级缓存(?)存放创建完成,初始完成的bean
/** Cache of singleton objects: bean name to bean instance. */
private final Map singletonObjects = new ConcurrentHashMap<>(256);
//三级缓存(?)实例创建完,通过lamdba表达式,封装成ObjectFactory
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map> singletonFactories = new HashMap<>(16);
//二级缓存(?),循环依赖中用到,三级缓存中取出刚实例化的bean
/** Cache of early singleton objects: bean name to bean instance. */
private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);
流程分析
总体流程 截图工具有限,无法截长图,答题流程就是如下,创建A->发现需要B->创建B...上面已经给了大概的流程图了,感觉已经很详细了,这里就贴一下程序调用栈
核心程序为
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
前面Spring源码也是详细分析了,感兴趣的可以自行阅读,这里呢讲一个大概流程。
创建(实例化)Bean->加入三级缓存->依赖注入->初始化->循环依赖检查->返回
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {// Instantiate the bean.持有创建出来的Bean对象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {//单例,先把缓存中的同名Bean清除
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//TODO 实际创建bean
if (instanceWrapper == null) {
//可以理解成调用构造函数创建实例,封装进了BeanWrapper
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 缓存单例,以便即使在由 BeanFactoryAware 等生命周期接口触发时也能解析循环引用。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//加入所谓的三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}// Initialize the bean instance.|依赖注入在这里发生
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
//bean初始化,调用调用各种前置处理,后置处理等等
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}//如果允许解决循环依赖的标识
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
//依赖这个bean的所有bean
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
//如果依赖这个bean的bean创建完成,而初始化完成的bean和注入给其他bean不一样,spring就认为是异常了
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}return exposedObject;
}
查询缓存 对于循环依赖A->B->C->D->A到最后一步创建A的时候,这时候肯定不能继续下去,因为再下去就死循环了
这个时候缓存的情况:
singletonObjects
(一级缓存中)没有A,earlySingletonObjects
(二级缓存中)没有A,singletonFactories
(三级缓存中)有A此时
singletonFactories
(三级缓存中)有但是还没有初始化,这个时候,就把还没有初始化的bean返回。让其他几个完成创建挨个出栈。三级缓存也相应的删除,加入到二级缓存中,到时候普通缓存查询也能够返回该实例。文章图片
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock|快速检没有锁的现有实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock|在完整的单例锁中一致地创建早期引用
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
至此,循环依赖总体流程分析结束。下面我们那一个同事询问我的bug分析分析
@Async引发的bug分析 报错代码贴了,就是上面
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
中下面抛出异常的那一段//如果允许解决循环依赖的标识
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
//依赖这个bean的所有bean
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
//如果依赖这个bean的bean创建完成,而初始化完成的bean和注入给其他bean不一样,spring就认为是异常了
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
那肯定
exposedObject == bean
不相等了。exposedObject = initializeBean(beanName, exposedObject, mbd);
跟着源码进入到`
AsyncAnnotationBeanPostProcessor
处理的时候,返现返回了一个CgLib代理对象,这时候,剩下的逻辑就一目了然了,实例化后的bean和注入给其他bean不一致,这时候就有问题了,spring认为应该是注入给其他bean的引用应当是一致的,前面放到二级缓存中了,其他bean创建的时候用的就是原始的引用,你初始化后返回了一个代理的引用,spring也就抛出了异常。也算是
@Async`的一个小bug。【「Spring源码」循环依赖解析&遇到的bug分析】分析完毕。也没啥好总结的,都是很简单的流程。觉得分析的风格还行的可以个一个赞
推荐阅读
- 推荐!Spring MVC面试题和答案整理
- 精品!Spring面试题和答案合集详解
- 史上最全!Spring Boot面试问题和答案整理
- Spring按业务模块输出日志到不同的文件
- android源码编译到刷机过程2
- android源码编译到刷机过程1
- spring 的 ApplicationContext.getBean(type)无法获取bean,报错
- 深入源码聊聊RocketMQ的刷盘机制
- OpenHarmerny 短彩信之Framework系统源码解析
- 使用@SpringBootApplication注解