spring中BeanPostProcessor引发的循环依赖异常

一 循环依赖问题

最近在项目中遇到如下异常

Bean with name ‘departmentServiceImpl’ has been injected into other beans [userServiceImpl] 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.

代码如下:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private DepartmentService  departmentService;
}

@Service
public class DepartmentServiceImpl implements DepartmentService {
    @Autowired
    private UserService userService;
 }

如果是这样引入,Spring会自己解决循环依赖。但是我对 DepartmentServiceImpl 进行了 后置处理器BeanPostProcessor 产生了一个 动态代理对象:

@Component
public class CustomPostProccessorr implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (Objects.equals(beanName,"departmentServiceImpl")) {


            return Proxy.newProxyInstance(CustomPostProccessorr.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return method.invoke(bean, args);
                }
            });
        }

        return bean;
    }
}

于是就出现了上面的异常。

二 产生问题的原因

1 三级缓存

Spring在解决bean的循环时,使用了三级缓存

1.1 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

存放已经创建好的单例bean。

1.2 二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

存放所有属性还未赋值完的bean或者bean的代理对象。

1.3 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

三级缓存存放的是如下 接口的

@FunctionalInterface
public interface ObjectFactory<T> {

	T getObject() throws BeansException;
}

存放到singletonFactories

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

getEarlyBeanReference 方法

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

这里是对创建的bean进行包装。获取spring容器中实现了 SmartInstantiationAwareBeanPostProcessor 接口的类,调用 getEarlyBeanReference 方法,对bean进行一系列处理,如: 产生代理对象 。如果不进行处理,直接就返回bean对象本身。

2 获取缓存bean

bean在创建之前,首先会去容器中获取缓存的bean,获取bean的代码如下:

	@Nullable
	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
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						// 调用 ObjectFactory的 getObject 方法,获取包装的bean或者bean本身。
						singletonObject = singletonFactory.getObject();
						// 把包装的bean或者bean放入到二级缓存中
						this.earlySingletonObjects.put(beanName, singletonObject);
						// 删除三级缓存中 bean对应的 ObjectFactory
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

如果没有循环依赖,创建一个bean调用该方法时,this.singletonFactories.get(beanName); 肯定返回的是 null
,因为一个bean对应的三级缓存中的ObjectFactory是在bean内存分配完成,还未对属性进行赋值之前加入到三级缓存中的。如下:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @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);
		}
		// 创建bean
		final 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.
		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");
			}
			// 设置bean对应的三级缓存中的 ObjectFactory
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			// 为 bean中的属性赋值
			populateBean(beanName, mbd, instanceWrapper);
			/**
			  * 初始化bean, 包括 
			  * 1. 对bean进行Aware接口的注入,
			  * 2. @PostConstruct 标注的方法初始化,
			  * 3. 调用spring 容器中 实现了 BeanPostProcessor 类的  postProcessBeforeInitialization 方法
			  * 4. 实现了 InitializingBean ,调用 afterPropertiesSet 方法
			  * 5. 如果使用了 xml,调用 bean标签中 init-method 标注的类
			  * 6. 调用spring 容器中 实现了 BeanPostProcessor 类的  postProcessAfterInitialization 方法
			 */
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
		}
}

如果this.singletonFactories.get(beanName); 的返回的不是 null ,则说明现在有循环依赖了,获取ObjectFactory,调用getObject 方法,获取经过 spring 容器中 SmartInstantiationAwareBeanPostProcessor 类处理过后的bean,放入到二级缓存中,删除三级缓存中的ObjectFactory。

3. 循环bean创建过程
  • (1) 创建 DepartmentServiceImpl, 分配完内存,在三级缓存singletonFactories中设置 bean的三级缓存ObjectFacotory,再进行属性赋值时发现依赖UserService。
  • (2) 创建 UserServiceImpl , 分配完内存,在三级缓存singletonFactories中设置 bean的三级缓存ObjectFacotory,再进行属性赋值时发现依赖DepartmentService。
  • (3) 创建 DepartmentServiceImpl 调用 getSingleton 获取缓存的bean时,发现有对应的三级缓存,走 this.singletonFactories.get(beanName); 不为 null的逻辑,把包装的bean放入到二级缓存中,同时返回包装的bean,对 UserServiceImpl 对象中的 DepartmentService 属性赋值,之后 UserServiceImpl 创建完成。
  • (4) 再对 DepartmentServiceImpl 中的属性使用spring 容器中已经创建完成的UserServiceImpl 对 UserService 赋值,之后完成 DepartmentServiceImpl 的创建。

以上是有循环依赖时spring创建bean的过程。但是为什么对 DepartmentService添加了一个 BeanPostProcessor 之后,会有开头的异常呢?看如下在bean初始化完成之后的代码:

// Initialize the bean instance.
Object exposedObject = bean;
try {
	// 为属性赋值
	populateBean(beanName, mbd, instanceWrapper);
	/**
	  * 初始化bean, 包括 
	  * 1. 对bean进行Aware接口的注入,
	  * 2. @PostConstruct 标注的方法初始化,
	  * 3. 调用spring 容器中 实现了 BeanPostProcessor 类的  postProcessBeforeInitialization 方法
	  * 4. 实现了 InitializingBean ,调用 afterPropertiesSet 方法
	  * 5. 如果使用了 xml,调用 bean标签中 init-method 标注的类
	  * 6. 调用spring 容器中 实现了 BeanPostProcessor 类的  postProcessAfterInitialization 方法
	 */
	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) {
	// 获取在二级缓存中引用的bean
	Object earlySingletonReference = getSingleton(beanName, false);
	if (earlySingletonReference != null) {
	 	// 判断在初始化过程中是否创建了代理对象
		if (exposedObject == bean) {
			// 没有创建代理对象,直接使用bean返回
			exposedObject = earlySingletonReference;
		}
		else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
		   // 获取有依赖的bean
			String[] dependentBeans = getDependentBeans(beanName);
			Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
			for (String dependentBean : dependentBeans) {
				if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
					actualDependentBeans.add(dependentBean);
				}
			}
			// 有依赖的bean,则直接抛异常
			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进行了替换,如创建了代理对象。就会使得 exposedObject == bean 不成立, 并且在bean创建过程中有循环依赖,就会抛 BeanCurrentlyInCreationException 异常。spring 在这里抛异常的原因是:

bean在创建过程中,循环依赖另外一个bean,而另外一个bean中引用了未初始化之前的当前bean。如果在初始化的时候对当前bean进行了替换,就会导致循环依赖的另外一个bean引用的当前bean与容器中的当前bean不一致,一个是替换之前的bean,一个是替换之后的bean。

三 问题的解决

之所以会触发这个异常,就是因为我们在为循环依赖的另外一个bean的当前引用赋值时使用的是原始的bean,可以把对bean的包装放在这个赋值之前,这时,我们再来看 三级缓存的第三级缓存,其实就是给我们机会,对bean进行包装,处理,默认spring不做任何处理。把代码改成如下方式,可以解决为departmentServiceImpl创建代理对象的问题 :

@Component
public class CustomSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {

        if (Objects.equals(beanName,"departmentServiceImpl")) {
            return Proxy.newProxyInstance(CustomPostProccessorr.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return method.invoke(bean, args);
                }
            });
        }
        return bean;
    }
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值