今天我们来探讨一个有意思的spring源码问题,也是一个学生告诉了我现象我从源码里面找到了这个有意思的问题。
首先我们看service层的代码案例,如下:
@Service("transationServiceImpl") public class TransationServiceImpl implements TransationService { @Autowired TransationService transationService; @Transactional @Async @Override public void transation() { } }
在transation方法上面加上了@Transactional和@Async两个注解,然后在TransationServiceImpl 类中自己把自己的实例注入到transationService属性中,存在循环依赖,理论上单例的循环依赖是允许的。但是我们启动容器会报错,测试代码如下:
public class MyTest { @Test public void test1() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanBean.class); } } @Component @ComponentScan(basePackages = {"com.xiangxue"}) public class ComponentScanBean { }
然后右键运行test1单元测试加载spring容器就会报错,报错信息如下:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘transationServiceImpl': Bean with name ‘transationServiceImpl' has been injected into other beans [transationServiceImpl] 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.
从报错的字面意思来看,是存在了多版本的循环依赖,如果要解决这个问题,我们必须追溯到源码中。
首先我们从TransationServiceImpl 实例化开始讲起。
实例化从getBean方法看起,前面代码我就不贴了,这篇文章是给读过spring源码的人看的,没读过也看不懂,哈哈 。
1、首先第一次创建TransationServiceImpl实例的时候会从缓存中获取实例 ,如果缓存里面有实例则直接返回,第一次创建的时候缓存中是没有实例的,所以会走到else代码块中。
这里是从三个缓存中获取实例化的详细代码。后面会分析
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { //根据beanName从缓存中拿实例 //先从一级缓存拿 Object singletonObject = this.singletonObjects.get(beanName); //如果bean还正在创建,还没创建完成,其实就是堆内存有了,属性还没有DI依赖注入 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //从二级缓存中拿 singletonObject = this.earlySingletonObjects.get(beanName); //如果还拿不到,并且允许bean提前暴露 if (singletonObject == null && allowEarlyReference) { //从三级缓存中拿到对象工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //从工厂中拿到对象 singletonObject = singletonFactory.getObject(); //升级到二级缓存 System.out.println("======get instance from 3 level cache->beanName->" + beanName + "->value->" + singletonObject ); this.earlySingletonObjects.put(beanName, singletonObject); //删除三级缓存 this.singletonFactories.remove(beanName); } } } } return singletonObject; }
2、第一次进来缓存中没有则创建TransationServiceImpl的实例
最终会走到doCreateBean方法中进行实例化,部分代码如下
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ............非关键代码不贴了 // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. //是否 单例bean提前暴露 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"); } //这里着重理解,对理解循环依赖帮助非常大,重要程度 5 添加三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //ioc di,依赖注入的核心方法,该方法必须看,重要程度:5 populateBean(beanName, mbd, instanceWrapper); //bean 实例化+ioc依赖注入完以后的html" target="_blank">调用,非常重要,重要程度:5 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)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { 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 " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } ............非关键代码不贴了 return exposedObject; }
由于业务类有循环依赖
所以在第一次实例化业务类的时候,在populateBean(beanName, mbd, instanceWrapper);进行依赖注入时会触发TransationServiceImpl业务类的getBean操作,也就是会调用TransationServiceImpl业务类的getBean方法,第二次会走到TransationServiceImpl实例化的逻辑中。这里明白的刷朵鲜花敲个1,哈哈。
但是在触发第二次业务类的getBean操作之前,还有一个非常重要的步骤,就是业务类的提前暴露,也就是三级缓存的建立。这块会建立业务类和ObjectFactory的映射关系这个建立映射关系是在依赖注入之前!!!!
3、循环依赖注入触发TransationServiceImpl类的第二次getBean获取实例化的逻辑
第二次进来的时候,由于第一次实例化的时候在三级缓存中建立了映射关系,所以第二次会从缓存中获取实例
ObjectFactory对象的getObject方法就会调用到。getEarlyBeanReference方法,这个方法是会从BeanPostProcessor中获取实例,这里可能就会返回代理实例
三级缓存的getObject方法会调用到getEarlyBeanReference中,断点一下,看看。
从断点看,
3:是获取事务代理的BeanPostProcessor类型是SmartInstantiationAwareBeanPostProcessor类型的,所以事务代理的BeanPostProcessor会进来,然后生成代理
4:是获取@Async异步代理的BeanPostProcessor,但是不是SmartInstantiationAwareBeanPostProcessor类型的,所以这里if就不会进来,所以最后这里从三级缓存中拿到的是事务切面的代码对象,注意这里是类中的依赖注入的实例是事务切面的代理实例,如图:
可以看到,这里的advisors切面容器明显是一个事务切面,所以业务类中依赖注入的是一个事务切面的代理实例。
但是在这里我还是要说一下,在生成事务代理的时候其实是有做缓存的,如下代码:
这里的cacheKey就是TransationServiceImpl业务类的bean的名称的字符串,然后会把这个字符串加入到一个earlyProxyReferences的Set容器中
在这里已经在TransationServiceImpl的第二次getBean的时候从三级缓存中获取到了代理对象了,那么第二次的实例化已经完成了,并且已经依赖注入到了TransationServiceImpl的属性中了,这时候依赖注入已经完成了,好,我们还是接着第一次TransationServiceImpl的实例来讲,贴代码:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ............非关键代码不贴了 // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. //是否 单例bean提前暴露 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"); } //这里着重理解,对理解循环依赖帮助非常大,重要程度 5 添加三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //ioc di,依赖注入的核心方法,该方法必须看,重要程度:5 populateBean(beanName, mbd, instanceWrapper); //bean 实例化+ioc依赖注入完以后的调用,非常重要,重要程度:5 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)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { 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 " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } ............非关键代码不贴了 return exposedObject; }
也就是populateBean(beanName, mbd, instanceWrapper);依赖注入已经完成了,代码接着往下走。
代理会执行到:
//bean 实例化+ioc依赖注入完以后的调用,非常重要,重要程度:5 exposedObject = initializeBean(beanName, exposedObject, mbd);
在这里,业务类会在这个方法里面再次生成代理,这里就有意思了。代码如下
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { //调用Aware方法 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //对类中某些特殊方法的调用,比如@PostConstruct,Aware接口,非常重要 重要程度 :5 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //InitializingBean接口,afterPropertiesSet,init-method属性调用,非常重要,重要程度:5 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { //这个地方可能生出代理实例,是aop的入口 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
在这个方法里面可能会生成业务类的代理,我们看看这个方法:
@Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
我们断点看看情况
**效果跟我们预期的一样,第一次实例化的时候,在属性依赖注入的时候会在三级缓存中获取事务的代理对象,从断点看,里面的属性确实是一个事务的代理对象,自己本身是没生成代理的。
由于方法上面有 @Transactional @Async在,3,4两个AOP入口的BeanPostProcessor中会生成相应的代理对象,这里为什么会生成代理对象,就不赘述了,核心思想是获取所有advisors,然后挨个判断advisors的pointCut是否matches这两个注解,matches的思路是看方法上面是否有@Transactional 或@Async注解,如果有则返回true就匹配了,如果能找到匹配的切面则生成bean的代理,但是这里要注意的是,事务切面在这里就不会生成代理了,为什么呢???**看代码
这里会判断earlyProxyReferences的Set容器中是否有这个cacheKey,这个cacheKey就是类的名称,而这个容器在提前暴露的三级缓存获取实例的时候就已经设置进去了,所以Set容器中是有这个类的
所以3的AOP入口这里会原样返回Bean,如图:
OK,有意思的来了,这时候就轮到4这个BeanPostProcessor的异步切面的AOP入口执行了。如图:
在这里就返回了bean的异步切面代理,实例如图:
我解释一下这个截图内容,
exposedObject是异步代理对象,在targetSource是代理对象的目标对象,目标对象中有一个transationService属性,这个属性是一个事务的代理对象,OK,从这里我们发现,我去,一个同样的类,居然生成了两个不同的代理对象,一个是异步的代理对象,一个是事务的代理对象,代理对象居然不一致了。为什么会这样,前面我已经分享得很清楚了
然后在spring中,这种情况默认是不被允许的,代码如下:
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { 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 " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
Object earlySingletonReference = getSingleton(beanName, false);
这里我们前面分析过,这里会从三级缓存中获取到事务代理对象
if (exposedObject == bean) { exposedObject = earlySingletonReference; }
然后这里有个if判断,bean是第一次实例化的bean,是没被initializeBean代理之前的bean
而exposedObject对象是一个异步切面的代理对象
这里两者是不相等的,而这个变量默认是allowRawInjectionDespiteWrapping=false的
所有这里就会抛异常,就是文章前面的那个异常,所有我们找到了为什么会有这么一个异常的出现了。
其实要解决这个异常也比较简单,只要把allowRawInjectionDespiteWrapping这个属性变成true就行了。
如何变了,代码如下:
这是这个变量就为true了 ,就不会抛异常了
但是就会存在一个现象,单元测试中获取到的bean对象和类中依赖注入的对象不是同一个了
这个bean对象是异步代理对象
类中属性的对象是事务切面的代理对象
有意思吧,哈哈 。
如果在类里面没有@Async异步注解,其实就不会有问题,默认是允许单例循环依赖的,为什么没问题
@Service("transationServiceImpl") public class TransationServiceImpl implements TransationService { @Autowired TransationService transationService; @Transactional @Override public void transation() { System.out.println(transationService.hashCode()); System.out.println("s"); } }
因为
if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { 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 " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } }
如果只要存在循环依赖,第一次业务类实例化的时候代理对象就是从这里获取的
这个地方
//bean 实例化+ioc依赖注入完以后的调用,非常重要,重要程度:5 exposedObject = initializeBean(beanName, exposedObject, mbd);
由于三级缓存中建立了缓存了
所以会直接返回对应的bean,没有生成代理。代理对象是从这个获取的
是从提前暴露的三级缓存中获取的代理对象赋值给了第一次实例化的bean对象,所以这个else if中可能出现异常的地方就不会走了,因为这两个bean exposedObject 和 bean是相等的。
到此这篇关于带有@Transactional和@Async的循环依赖问题的解决的文章就介绍到这了,更多相关@Transactional和@Async的循环依赖内容请搜索小牛知识库以前的文章或继续浏览下面的相关文章希望大家以后多多支持小牛知识库!
问题内容: 我已经成功地将AngularJs与OOP结合使用了一段时间,所提供的方法允许您将类定义为angular服务,以后可以像这样扩展或继承: 使用所描述的方法使您能够定义完美地集成到角度基础架构中的类。您可以从OOP和AngularJs这两个世界获得各种漂亮的功能。依赖注入对于您的类是免费的,它使您的类变得简单,允许将许多样板控制器代码放入某些基类中,以便以后重用。 然而 AngularJs
问题内容: 我正在设计一个系统,其中包含两个模块,一个模块孕育文件,另一个模块。对于某些逻辑运算,它们需要彼此提供的服务。 每个模块都由一个单例表示,该单例实现一个接口,该接口向彼此提供一些服务,并带有抽象工厂来提供它们,如下所示: 文件模块的主类是这样的: 我不确定自己是否正确处理了循环依赖。有什么办法可能会意外中断? 编辑 :正如下面已回答的那样,处理此问题的正确方法是注射。但是,解决此问题的
我在一个ARM模板中有两个相互依赖的Azure资源:一个密钥库和一个service fabric集群。 是否有一种方法可以引用service fabric集群的对象ID来提供给密钥库的访问策略,是否有一种方法可以在不硬编码任何值的情况下生成密钥库中的机密?理想情况下,我们只知道秘密名称,并且只将该秘密名称提供给ARM模板中的服务fabric集群。
存在依赖循环问题。以下是Spring框架的信息: 但当我添加@Lazy时,问题就解决了。我几乎无法在其他地方恢复这种现象,我也不知道原因。如果你能早点给我解释,我将不胜感激。
我正在使用Spring Boot数据jpa和hazelcast开发Spring Boot 2.4.1项目。我正试图建立一个分布式地图,并读取数据库。我已经实现了,但当我尝试运行应用程序时,由于循环依赖关系,它无法启动。似乎JpaRepository需要HazelcastInstance首先可用,但HazelcastInstance需要MapLoader,MapLoader反过来需要JpaRepos
问题内容: 我正在将Java项目从Ant迁移到Gradle。我认为最好的解决方案是使用Gradle的多项目支持,但是我找不到摆脱循环依赖的方法。 原始项目已设置为具有以下布局: 之间的关系,并且,是棘手的。将取决于或根据配置文件。同样,无论配置属性如何,都依赖和。并且永远不会在同一时间建造。 我认为一种快速的解决方案是在: 接下来,我想过要找到一种方法来使之更接近公正工作。这导致我想到了这一点: