Spring中Bean的循环依赖
在Spring中Bean的循环依赖是一个非常重要的知识点,特别是在最近的工作中遇到过,因Bean的循环依赖问题而踩到的一个坑、坑、坑!!!(重要的事情说三遍),同时也想着为下一篇埋一个伏笔,本文主要还是以Bean的循环依赖原理方面展开论述。
1、Bean对象创建过程
通过以下简要的流程图,加深对Bean对象创建流程的理解。
2、Bean对象之间循环依赖的产生
产生的原因:可能因某种业务的需求,开发人员在代码编写时会存在如下情况:
- ServcieA依赖于ServiceB,而ServiceB依赖ServiceA。
- ServiceA依赖于ServiceA(Bean对象的自我依赖)。
- 如下图所示
Spring中默认对象与对象之间是不允许存在相互依赖的,但是本身提供了Bean对象之间相互依赖的解决方案,即就是通过参数配置进行控制:spring.main.allow-circular-references = true , 默认是false。
3、采用三级缓存解决Bean之间循环依赖
// 一级缓存(用来存放已经初始化好的Bean)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存(用来存放Bean的早期引用)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存(用来存放Bean对应的ObjectFactory,即获取Bean的早期引用)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
3.1 Bean存入三级缓存中
/*
* 存入三级缓存条件:对象是单例 && 当前beanName处于创建中 && this.allowCircularReferences = true
* this.allowCircularReferences 由setting spring.main.allow-circular-references属性控制
*/
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()方法逻辑:
* 1、当前beanName添加至三级缓存中
* 2、当前beanName从二级缓存中移除
* 3、当前beanName添加至this.registeredSingletons中
*
* ObjectFactory<?> singletonFactory 为函数式接口,在调用ObjectFactory的getObject方法时,开始调用getEarlyBeanReference()方法(该方法终端标记下)
*/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
3.2 Bean对象之间存在属性注入流程图
如上图所示,经过分析可以得到:
当不存在循环依赖时,二级缓存和三级缓存都没有用到,只有当存在循环依赖时,才会需要二级缓存和三级缓存。
3.3 从三级缓存中获取引用对象
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存中获取bean
Object singletonObject = this.singletonObjects.get(beanName);
// 一级缓存获取为空 且 当前beanName正在创建中 时 需要从二级缓存中获取
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 从二级缓存中获取bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存为空 且 allowEarlyReference 等于 true时
if (singletonObject == null && allowEarlyReference) {
// DCL : 二次校验当前bean是否为空
synchronized (this.singletonObjects) {
// 一级缓存获取Bean
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 二级缓存获取Bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 三级缓存中获取Bean
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// singletonFactory 不为空时 通过getObject()方法获取增强后的对象实例
if (singletonFactory != null) {
// 调用getObject方法实际上 调用:getEarlyBeanReference()方法获取代理bean
singletonObject = singletonFactory.getObject();
// 得到代理bean 存储到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 移除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
4、Spring中AOP增强处理
4.1 基于SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference()方法
主要实现类:AbstractAutoProxyCreator, 类图关系如下
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 存储早期代理Bean的引用
this.earlyProxyReferences.put(cacheKey, bean);
// 执行返回AOP代理后的bean
// 主要处理:
// 1、基于AnnotationAwareAspectAutoProxyCreator,即针对与AOP切面,对应注解:@AspectJ
// 2、基于事务注解:@Transactional
return wrapIfNecessary(bean, beanName, cacheKey);
}
经过上述代码分析可知。会存在以下两种情况(即存入二级缓存的对象)
1、通过AOP增强,则存入原始bean引用创建的代理对象。
2、没有AOP增强,则存入原始bean引用。
以上两点也是开头提到的坑的主要原因!!!
4.2 主要判断是否生成代理的主要实现类AopUtils#canApply()
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
// 获取当前Bean的class对象,目的是为了获取目标类对象的所有方法
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
// 遍历所有方法 查看目标类对象上或方法上是否matches方法是否为true
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
// 为true时,表示此时需要生成代理对象
return true;
}
}
}
return false;
}
4.3 BeanPostProcessor的postProcessBeforeInitialization方法
主要实现类:AsyncAnnotationBeanPostProcessor,类图关系如下:
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
/**
* isEligible方法的核心逻辑为:AopUtils.canApply()方法
*/
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
else {
advised.addAdvisor(this.advisor);
}
return bean;
}
}
/**
* isEligible方法的核心逻辑为:AopUtils.canApply()方法
*
* 若isEligible返回为false时,则表示无需代理
*/
if (isEligible(bean, beanName)) {
// 如下为创建代理类对象的相关逻辑
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return proxyFactory.getProxy(classLoader);
}
// No proxy needed.
return bean;
}
4.4 AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization()方法
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 此处由于getEarlyBeanReference()方法在执行的时候添加了早期引用
// 所以,此处传入的bean和this.earlyProxyReferences.remove(cacheKey)获得的bean 是同一个
// 也可以理解为没有执行wrapIfNecessary()方法
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 执行生成代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
4.5 Bean初始化完成后核心代码解读
/**
* Bean完成初始化之后核心代码解析,
* 当早期引用的Bean和初始化后的Bean不相等时,会报Bean的循环依赖异常BeanCurrentlyInCreationException
*/
if (earlySingletonExposure) {
/**
* 此处是从一(二)级缓存中获取对象,主要还是从二级获取,
* 因为参数allowEarlyReference==false
*/
Object earlySingletonReference = getSingleton(beanName,false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 此处是AOP切面支持对象循环依赖的原因
// earlySingletonReference:是从二级缓存获得的bean (代理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 " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
综上所述,我从四个方面解读了Spring中循环依赖的工作原理,若是能从上往下阅读,相信你对循环依赖这块也会有一定的掌握,本篇文章主要是针对原理和疑点方面进行解读,并不是结合所有代码进行方方面面的研读,这一方面希望童鞋们能够知道,具体的源码方面研读的话,到后面有时间的话,抽出核心部分研读再分享出来。
这篇文章我也是经过了两周时间的研读,有不对的地方望童鞋们批评指正! 若是有帮助到您,希望您能帮忙转发和点赞哦,说明我的辛苦没有白费,后面我也会继续努力,提升自己,进而输出更多的干货啦!