@Transaction与@Async 循环依赖错误 ,@Lazy 是怎么解决这个错误的

在我们同时使用 @Transaction与@Async 一不小心就会触发 BeanCurrentlyInCreationException(多版本循环依赖的错误) ,
也就是说spring缓存中单例对象有多个版本,这违反了,单例原则
先开一下对象的在spring中的内存图

例如下面这个代码:


@Component
public class AsynService {
    @Autowired
    private AsynService asynService;

    @Transactional
    @Async
    public void a(){
    }
}

我们先看一看这个错误是怎么被触发的
第一次 getBean() 一步一步的跟 会进入到这个方法
AbstractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
		throws BeanCreationException {
		删除部分无法代码	
		....
		
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//设置三级缓存
			addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
			});
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			// 依赖注入
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				//这里会进行AOP
				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<String>(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.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

在 populateBean() 中会进行依赖注入,注入属性 asynService 会第二次getBean(),第二次会尝试从缓存中拿。
下面是从缓存拿取的代码
DefaultSingletonBeanRegistry#getSingleton

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//从一级缓存中获取
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
			//从二级缓存中获取
				singletonObject = this.earlySingletonObjects.get(beanName);
				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 != NULL_OBJECT ? singletonObject : null);
	}

最后会从三级缓存中拿,三级缓存获取时会进入下面的方法
AbstractAutowireCapableBeanFactory#getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				//BeanPostProcessor 埋点,这个地方可能会产生代理对象
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					//只有AOP切面,就会产生代理对象
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
					if (exposedObject == null) {
						return null;
					}
				}
			}
		}
		return exposedObject;
	}

这个方法我们本次只需要关注 AbstractAutoProxyCreator.class,下面是代码
AbstractAutoProxyCreator.getEarlyBeanReference

public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		//这里 可以理解为 cacheKey 就是 beanName
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
			//缓存提前暴露的 对象Key,记住这里后面会使用到
			this.earlyProxyReferences.add(cacheKey);
		}
		//代理的创建
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

下面我们看看 wrapIfNecessary() 干了些什么

AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//删除了部分无关的代码
		...

		// Create proxy if we have advice.
		//获取这个当前类(AsynService)是否有切面,这里会找到一个@Transaction 相关的切面
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			//生成代理
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

wrapIfNecessary 会生成代理 我们就把它叫做 aopPoxy ,接着会把代理对象(aopPoxy )放入到二级缓存,并删除三级缓存,到此第二次getBean()就完成了,同时第一次getBean() 执行 populateBean()也就(本次关心的流程)完成了,现在进入了initializeBean

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
	//删除部分无关代码
	...

		if (mbd == null || !mbd.isSynthetic()) {
			//BeanPostProcess 的 postProcessAfterInitialization() 方法埋点
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}

我在本次只需要关注两 BeanPostProcess
1.AbstractAutoProxyCreator#postProcessAfterInitialization
因为第二次创建时 向earlyProxyReferences 设置了key,因此,这个方法返回的是bean本身

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

2.AsyncAnnotationBeanPostProcessor#postProcessAfterInitialization
Async 的入口类 ,这个返回的是代理 ,我们就把它叫做 asyncPoxy

public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (bean instanceof AopInfrastructureBean) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}

		if (bean instanceof Advised) {
			Advised advised = (Advised) bean;
			if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
				// Add our local Advisor to the existing proxy's Advisor chain...
				if (this.beforeExistingAdvisors) {
					advised.addAdvisor(0, this.advisor);
				}
				else {
					advised.addAdvisor(this.advisor);
				}
				return bean;
			}
		}

		if (isEligible(bean, beanName)) {
			ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
			if (!proxyFactory.isProxyTargetClass()) {
				evaluateProxyInterfaces(bean.getClass(), proxyFactory);
			}
			proxyFactory.addAdvisor(this.advisor);
			customizeProxyFactory(proxyFactory);
			//生成代理
			return proxyFactory.getProxy(getProxyClassLoader());
		}

		// No async proxy needed.
		return bean;
	}

到此我们就获取到了两个代理:
1.第二getBean() 二级缓存中的 aopPoxy,
2.第一次getBean() 时initializeBean()f方法返回的 asyncPoxy
下面是触发逻辑代码

if (earlySingletonExposure) {
			//从缓存中获取,此时可以获取到二级缓存中的aopPoxy
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				//这里的 exposedObject  = asyncPoxy ,bean = bean
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					//最后会进入这里,然后就会抛出异常
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<String>(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.");
					}
				}
			}
		}

我们现在了解了导致 BeanCurrentlyInCreationException 的原因,最后总结一下:
1.依赖注入导致 二级缓存中有 aopPoxy
2. 埋点 postProcessAfterInitialization 会 产生 exposedObject = asyncPoxy
3. 最后判断 发现 缓存中有 aopPoxy,并且 exposedObject != bean 异常就触发了

要解决不触发异常,我们就有只有让判断不成立就行了,因此 要么让 @Async 使用 aop 的入口类,
要么让缓存中没有值,入口了是Async 模块定义好的,我们不好处理,最简单的办法就是让缓存中没有数据
刚好@Lazy就能实现,让缓存中没有数据
接下来我们看看@Lazy 是怎实现的
populateBean() 方法中如果注入点被@Lazy 修饰就不会触发getBean(),而是生成一个代理对象直接赋值给注入的对象,不getBean() 就三级缓存就不会有数据,异常判断就不成立了
下面是@Lazy 的核心代码
ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy

	protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final String beanName) {
		Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
				"BeanFactory needs to be a DefaultListableBeanFactory");
		final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
		//删除部分代码
		....
		ProxyFactory pf = new ProxyFactory();
		pf.setTargetSource(ts);
		Class<?> dependencyType = descriptor.getDependencyType();
		if (dependencyType.isInterface()) {
			pf.addInterface(dependencyType);
		}
		//生成代理
		return pf.getProxy(beanFactory.getBeanClassLoader());
	}

在这里插入图片描述

最后我想说:@Async 与其他产生代理的注解最好不要写在一个类中,如果写在一起最后类中的注入点 都加上@Lazy

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值