admin管理员组

文章数量:1534202

最近在项目中使用@Async注解在方法上引起了循环依赖报错。

代码类似如下:

package com.morris.spring.entity.circular;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
public class SingleFieldBeanD {
	@Autowired
	private SingleFieldBeanD singleFieldBeanD;

	@Async
	public void test() {
		System.out.println("test");
	}
}

报错内容如下:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'singleFieldBeanD': Bean with name 'singleFieldBeanD' has been injected into other beans [singleFieldBeanD] 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.

	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:655)
... ...

其实当我们使用Spring的时候,默认是会解决循环依赖,如果不加@Async注解,程序就能正常运行,一旦加上了@Async注解方法后,处理循环依赖就失效了,就会抛出异常。

循环依赖错误原因分析

源码分析,查看异常抛出时的方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			// 实例化
			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 {
					/**
					 * 使用下面的BeanPostProcessor收集带有注解的方法,方便下面初始化属性时调用
					 * AutowiredAnnotationBeanPostProcessor @Autowired @Value
					 * CommonAnnotationBeanPostProcessor @Resource @PostConstruct @PreDestroy
					 */
					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.
	// 处理循环依赖,实例化后放入三级缓存
	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 {
		// 初始化bean,为bean赋予属性
		populateBean(beanName, mbd, instanceWrapper);
		// 调用初始化方法
		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) {
			// 不为空说明产生了循环依赖
			// bean是通过构造方法创建的
			// 而exposedObject是initializeBean方法返回,这里有可能产生aop代理后返回
			if (exposedObject == bean) {
				// 相等说明没有产生aop代理
				exposedObject = earlySingletonReference;
			}
			else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				// 产生了aop代理,且有对象依赖他
				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 " +
							"'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通过属性形成了循环依赖:

  1. 创建A:使用构造方法构造A,将A放入三级缓存,给A的属性B复赋值
  2. 创建B:使用构造方法构造B,将B放入三级缓存,给B的属性A复赋值
  3. 此时因为A支持循环依赖,所以会从三级缓存中获取A填充到B的属性中,此时B的属性A持有的是A的原始对象的引用
  4. B完成其他属性的赋值及初始化方法的调用
  5. 继续完成了A的属性的赋值(此时已持有B的实例的引用),继续执行初始化方法initializeBean(…),在此处会解析@Aysnc注解,从而生成一个代理对象,所以最终exposedObject是一个代理对象(而非原始对象)最终加入到容器里
  6. 最后B引用的属性A是个原始对象,而此处准备return的实例A竟然是个代理对象,也就是说B引用的并非是最终对象(不是最终放进容器里的对象),最后抛出异常。

循环依赖问题的解决方案

allowRawInjectionDespiteWrapping设置为true

从源码中可以发现有一个allowRawInjectionDespiteWrapping变量来控制是否进入else if代码段,最后抛出异常,此字段默认为false,如果将此字段设置为true,就不会进入else if代码段,从而就不会抛出异常了。

设置方法如下:

AbstractAutowireCapableBeanFactory beanFactory = (AbstractAutowireCapableBeanFactory) applicationContext.getBeanFactory();
beanFactory.setAllowRawInjectionDespiteWrapping(true);

属性上面添加@Lazy注解

在属性上面添加@Lazy注解就能打破循环依赖,就不会抛出异常了。

代码如下:

package com.morris.spring.entity.circular;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
public class SingleFieldBeanD {
	@Autowired
	@Lazy
	private SingleFieldBeanD singleFieldBeanD;

	@Async
	public void test() {
		System.out.println("test");
	}
}

为什么加上@Lazy就不会抛出异常了呢?因为此时注入的是代理对象,而不是原始对象,不构成循环依赖。

且看源码:

在对属性进行注入时会执行下面的代码:
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
	// buildLazyResourceProxy里面还有调用了getResource方法从Spring容器中获取bean
	return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
			getResource(this, requestingBeanName));
}

lazyLookup什么时候为true呢?属性上面有@Lazy注解,lazyLookup=true

private class ResourceElement extends LookupElement {
	private final boolean lazyLookup;
	public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
		super(member, pd);
		Resource resource = ae.getAnnotation(Resource.class);
		String resourceName = resource.name();
		Class<?> resourceType = resource.type();
		this.isDefaultName = !StringUtils.hasLength(resourceName);
		if (this.isDefaultName) {
			resourceName = this.member.getName();
			if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
				resourceName = Introspector.decapitalize(resourceName.substring(3));
			}
		}
		else if (embeddedValueResolver != null) {
			resourceName = embeddedValueResolver.resolveStringValue(resourceName);
		}
		if (Object.class != resourceType) {
			checkResourceType(resourceType);
		}
		else {
			// No resource type specified... check field/method.
			resourceType = getResourceType();
		}
		this.name = (resourceName != null ? resourceName : "");
		this.lookupType = resourceType;
		String lookupValue = resource.lookup();
		this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
		// 有@Lazy注解,lazyLookup=true
		Lazy lazy = ae.getAnnotation(Lazy.class);
		this.lazyLookup = (lazy != null && lazy.value());
	}
... ...

属性上面如果有@Lazy注解,lazyLookup=true,就会创建代理:

protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) {
	TargetSource ts = new TargetSource() {
		@Override
		public Class<?> getTargetClass() {
			return element.lookupType;
		}
		@Override
		public boolean isStatic() {
			return false;
		}
		@Override
		public Object getTarget() {
			// 从Spring容器中获得bean
			return getResource(element, requestingBeanName);
		}
		@Override
		public void releaseTarget(Object target) {
		}
	};
	// 创建Aop代理
	ProxyFactory pf = new ProxyFactory();
	pf.setTargetSource(ts);
	if (element.lookupType.isInterface()) {
		pf.addInterface(element.lookupType);
	}
	ClassLoader classLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
			((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : null);
	return pf.getProxy(classLoader);
}

从ApplicationContext获取对象

在方法具体使用对象时再去Spring容器中获取对象,这样拿到的就是代理对象:

package com.morris.spring.entity.circular;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
public class SingleFieldBeanD implements ApplicationContextAware {
	
	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	public SingleFieldBeanD getSingleFieldBeanD() {
		return applicationContext.getBean(SingleFieldBeanD.class);
	}
	
	@Async
	public void test() {
		System.out.println("test");
	}


}

重构方法

重新建class,把@Async的方法放在新的类中,从根本上消除循环依赖。

本文标签: 报错SpringAsync