带有@Transactional和@Async的循环依赖问题的解决

今天我们来探讨一个有意思的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依赖注入完以后的调用,非常重要,重要程度: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的循环依赖内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring循环依赖的三种方式(推荐)

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 Spring容器会将每一个正在创建的Bean 标识符放在一个"当前创建Bean池"中,Bean标识符在创建过程中将一直保持 在这个池中,因此如果在创建Bean过程中发现自己已经在"当前创建Bean池"里时将抛出 BeanCurrentlyInCrea

  • 详解Spring Bean的循环依赖解决方案

    如果使用构造函数注入,则可能会创建一个无法解析的循环依赖场景. 什么是循环依赖 循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环.比如A依赖于B,B依赖于C,C又依赖于A.如下图: 注意,这里不是函数的循环调用,是对象的相互依赖关系.循环调用其实就是一个死循环,除非有终结条件. Spring中循环依赖场景有: (1)构造器的循环依赖 (2)field属性的循环依赖. 怎么检测是否存在循环依赖 检测循环依赖相对比较容易,Bean在创建的时候可以给该Bean打标,

  • Spring中循环依赖的解决方法详析

    前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚.本文就试着尽自己所能,对此做出一个较详细的解读.另,需注意一点,下文中会出现类的实例化跟类的初始化两个短语,为怕园友迷惑,事先声明一下,本文的实例化是指刚执行完构造器将一个对象new出来,但还未填充属性值的状态,而初始化是指完成了属性的依赖注入. 一.先说说Spring解决的循环依赖是什么 Java中的循环依赖分两种,一种是构造器的循环依赖,另一种是属性的循环依赖.

  • 浅谈Spring如何解决循环依赖的问题

    在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的.这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能够一下子思考出个中奥秘.本文主要针对这个问题,从源码的角度对其实现原理进行讲解. 1. 过程演示 关于Spring bean的创建,其本质上还是一个对象的创建,既然是对象,读者朋友一定要明白一点就是,一个完整的对象包含两部分:当前对象实例化和对象属性的实例化.在Spring中,对象的实例化是通过反射实

  • spring循环依赖策略解析

    循环依赖 所谓循环依赖就是多个Bean之间依赖关系形成一个闭环,例如A->B->C->...->A 这种情况,当然,最简单的循环依赖就是2个Bean之间互相依赖:A->B(A依赖B), B->A(B依赖A) .在Spring中,如果A->B,那么在创建A的过程中会去创建B,在创建B(或B的依赖)的过程中又发现B->A,这个时候就出现了循环依赖的现象. 循环依赖的解决 spring中的循环依赖只有当 1.Bean是单例, 2.通过属性注入的情况 这两个条件满足

  • 浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyIn CreationException异常表示循环依赖. 如在创建TestA类时,构造器需要TestB类,那将去创建TestB,在创建TestB类时又发现需要TestC类,则又去创建Test

  • 详解Spring循环依赖的解决方案

    spring针对Bean之间的循环依赖,有自己的处理方案.关键点就是三级缓存.当然这种方案不能解决所有的问题,他只能解决Bean单例模式下非构造函数的循环依赖. 我们就从A->B->C-A这个初始化顺序,也就是A的Bean中需要B的实例,B的Bean中需要C的实例,C的Bean中需要A的实例,当然这种需要不是构造函数那种依赖.前提条件有了,我们就可以开始了.毫无疑问,我们会先初始化A.初始化的方法是org.springframework.beans.factory.support.Abstra

  • 深入理解Spring中的循环依赖

    循环依赖 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA.形成一个环状引用关系. 在使用Spring时,如果主要采用基于构造器的依赖注入方式,则可能会遇到循环依赖的情况,简而言之就是Bean A的构造器依赖于Bean B,Bean B的构造器又依赖于Bean A.在这种情况下Spring会在编译时抛出Bean

  • 带有@Transactional和@Async的循环依赖问题的解决

    今天我们来探讨一个有意思的spring源码问题,也是一个学生告诉了我现象我从源码里面找到了这个有意思的问题. 首先我们看service层的代码案例,如下: @Service("transationServiceImpl") public class TransationServiceImpl implements TransationService { @Autowired TransationService transationService; @Transactional @Asy

  • Spring处理@Async导致的循环依赖失败问题的方案详解

    目录 简介 问题复现 原因分析 解决方案 方案1:懒加载 方案2:不让@Async的类有循环依赖 方案3:allowRawInjectionDespiteWrapping设置为true 为什么@Transactional不会导致失败 简介 说明 本文介绍SpringBoot中的@Async导致循环依赖失败的原因及其解决方案. 概述 我们知道,Spring解决了循环依赖问题,但Spring的异步(@Async)会使得循环依赖失败.本文将用实例来介绍其原因和解决方案. 问题复现 启动类 启动类添加@

  • 简单了解Spring循环依赖解决过程

    这篇文章主要介绍了简单了解Spring循环依赖解决过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚.本文就试着尽自己所能,对此做出一个较详细的解读.另,需注意一点,下文中会出现类的实例化跟类的初始化两个短语,为怕园友迷惑,事先声明一下,本文的实例化是指刚执行完构造器将一个对象new出来,但还未填充属性值的状态,而

  • Spring循环依赖的解决办法,你真的懂了吗

    介绍 先说一下什么是循环依赖,循坏依赖即循环引用,两个或多个bean相互引用,最终形成一个环.Spring在初始化A的时候需要注入B,而初始化B的时候需要注入A,在Spring启动后这2个Bean都要被初始化完成 Spring的循环依赖有两种场景 构造器的循环依赖 属性的循环依赖 构造器的循环依赖,可以在构造函数中使用@Lazy注解延迟加载.在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入 属性的循环依赖主要是通过3个map来解决的 构造器的循环依赖 @Component publi

  • Spring IOC原理补充说明(循环依赖、Bean作用域等)

    前言 通过之前的几篇文章将Spring基于XML配置的IOC原理分析完成,但其中还有一些比较重要的细节没有分析总结,比如循环依赖的解决.作用域的实现原理.BeanPostProcessor的执行时机以及SpringBoot零配置实现原理(@ComponentScan.@Import.@ImportSource.@Bean注解的使用和解析)等等.下面就先来看看循环依赖是怎么解决的,在此之前一定要熟悉整个Bean的实例化过程,本篇只会贴出关键性代码. 正文 循环依赖 首先来看几个问题: 什么是循环依

  • 浅谈Spring 解决循环依赖必须要三级缓存吗

    我们都知道 Spring 是通过三级缓存来解决循环依赖的,但是解决循环依赖真的需要使用到三级缓冲吗?只使用两级缓存是否可以呢?本篇文章就 Spring 是如何使用三级缓存解决循环依赖作为引子,验证两级缓存是否可以解决循环依赖. 循环依赖 既然要解决循环依赖,那么就要知道循环依赖是什么.如下图所示: 通过上图,我们可以看出: A 依赖于 B B 依赖于 C C 依赖于 A public class A { private B b; } public class B { private C c; }

  • spring 如何解决循环依赖

    首先解释下什么是循环依赖,其实很简单,就是有两个类它们互相都依赖了对方,如下所示: @Component public class AService { @Autowired private BService bService; } @Component public class BService { @Autowired private AService aService; } AService和BService显然两者都在内部依赖了对方,单拎出来看仿佛看到了多线程中常见的死锁代码,但很显然S

  • Spring源码剖析之Spring处理循环依赖的问题

    前言 你是不是被这个骚气的标题吸引进来的,_ 喜欢我的文章的话就给个好评吧,你的肯定是我坚持写作最大的动力,来吧兄弟们,给我一点动力 Spring如何处理循环依赖?这是最近较为频繁被问到的一个面试题,在前面Bean实例化流程中,对属性注入一文多多少少对循环依赖有过介绍,这篇文章详细讲一下Spring中的循环依赖的处理方案. 什么是循环依赖 依赖指的是Bean与Bean之间的依赖关系,循环依赖指的是两个或者多个Bean相互依赖,如: 构造器循环依赖 代码示例: public class BeanA

  • 关于Java Spring三级缓存和循环依赖的深入理解

    目录 一.什么是循环依赖?什么是三级缓存? 二.三级缓存如何解决循环依赖? 三.使用二级缓存能不能解决循环依赖? 一.什么是循环依赖?什么是三级缓存? [什么是循环依赖]什么是循环依赖很好理解,当我们代码中出现,形如BeanA类中依赖注入BeanB类,BeanB类依赖注入A类时,在IOC过程中creaBean实例化A之后,发现并不能直接initbeanA对象,需要注入B对象,发现对象池里还没有B对象.通过构建函数创建B对象的实例化.又因B对象需要注入A对象,发现对象池里还没有A对象,就会套娃.

随机推荐