Spring循环依赖(上) 背景 今天在开发过程中,同事在一个xxxService
中的某个方法上添加了@Async
注解,本意是希望这个方法可以异步执行,但是添加注解之后,发现启动报错。
报错信息如下:
Bean with name ‘xxxService’ has been injected into other beans [xxxAService,xxxBService,xxxCService,xxxDService,xxxEService] 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.
从报错信息中可以明显的知道,是因为spring bean
之间出现了循环依赖。以往只记得一个结论,spring
是支持循环依赖,且spring
中通过三级缓存来解决循环依赖出现的问题。正好借这个问题,且最近刚好在开spring
启动流程,对bean
的生命周期有大概的了解。
Bean的创建过程 实际上一个Spring Bean
的创建包含了三个过程:实例化、注入依赖项、初始化,在spring
中分别对应的是instantiation
、populate
、initialization
。在没有看spring
之前,我一度以为实例化和初始化是一个意思,一直以为两个单词是一个意思。
实例化(instantiation
):通过指定的静态函数或者实例化对象的某个函数,或者是无参构造函数等方式创建一个空对象。
注入依赖项(populate
):比如实现了BeanPostProcessor
的InstantiationAwareBeanPostProcessor
开放的扩展点进行特定情况的依赖项注入,比如在属性上标记的@Autowird
,则是通过AutowiredAnnotationBeanPostProcessor
进行注入处理。
初始化(initilization
):注入依赖项之后的一些操作,比如指定了初始化initMethod
方法,或者是BeanPostProcessor#postProcessBeforeInitialization
接口等。
Spring
的bean
的创建过程,主要是从AbstractApplicationContext#refresh
方法说起,这个方法涉及到了Spring bean
的整个生命周期。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 @Override public void refresh () throws BeansException, IllegalStateException { synchronized (this .startupShutdownMonitor) { prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { postProcessBeanFactory(beanFactory); invokeBeanFactoryPostProcessors(beanFactory); registerBeanPostProcessors(beanFactory); initMessageSource(); initApplicationEventMulticaster(); onRefresh(); registerListeners(); finishBeanFactoryInitialization(beanFactory); finishRefresh(); }catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } destroyBeans(); cancelRefresh(ex); throw ex; }finally { resetCommonCaches(); } } }
创建Bean
的三个过程,在AbstractAutowireCapableBeanFactory#doCreateBean
方法中均有体现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 protected Object doCreateBean (final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { if (instanceWrapper == null ) { instanceWrapper = createBeanInstance(beanName, mbd, args); } ... ... 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)); } Object exposedObject = bean; try { 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); } } }
下面逐个来简单分析下。
1.实例化对象 通过以下代码分析,实例化其实就是通过这几种方式,创建了一个空对象,并获取这个对象的引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 protected BeanWrapper createBeanInstance (String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null ) { return obtainFromSupplier(instanceSupplier, beanName); } if (mbd.getFactoryMethodName() != null ) { return instantiateUsingFactoryMethod(beanName, mbd, args); } boolean resolved = false ; boolean autowireNecessary = false ; if (args == null ) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null ) { resolved = true ; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null , null ); } else { return instantiateBean(beanName, mbd); } } Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } ctors = mbd.getPreferredConstructors(); if (ctors != null ) { return autowireConstructor(beanName, mbd, ctors, null ); } return instantiateBean(beanName, mbd); }
2.注入依赖项 主要的逻辑就是根据BeanPostProcessor
扩展点的接口,或者实现它的二次扩展接口。通过这些扩展接口的实现,来进行特殊方式的注入,比如使用@Autowird
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 protected void populateBean (String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { ... ... if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return ; } } } } ... ... PropertyDescriptor[] filteredPds = null ; if (hasInstAwareBpps) { if (pvs == null ) { pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null ) { if (filteredPds == null ) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null ) { return ; } } pvs = pvsToUse; } } } }
3.初始化对象 主要逻辑就是在注入依赖项之后,进行一些对象完善,可以通过制定初始化方法initMethod
或者通过实现BeanPostProcessor
后置处理器来处理一些操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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 { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { 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()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
小结: 从大方向来说,spring
创建bean
的过程就是以上三个过程。而循环依赖的主要处理逻辑应该是在注入依赖项这一环,判定是否循环依赖是在初始化方法之后进行判定。
三级缓存 在spring
中通过三级缓存来解决循环依赖,三级缓存分别为:
singletonObjects
:一级缓存,保存的是初始化完成的单例Bean
对象
earlySingletonObjects
:二级缓存,保存的是实例化之后,注入依赖项之前的对象,换句话说就是一个空对象的引用。
singletonFactories
:三级缓存,保存的是需要提前暴露的对象的单例工厂,二级缓存中的对象引用,是由这个工厂生成。
在AbstractAutowireCapableBeanFactory#doCreateBean
方法的,实例化对象之后,注入依赖项之前,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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)); } protected void addSingletonFactory (String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null" ); synchronized (this .singletonObjects) { if (!this .singletonObjects.containsKey(beanName)) { this .singletonFactories.put(beanName, singletonFactory); this .earlySingletonObjects.remove(beanName); this .registeredSingletons.add(beanName); } } } 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; }
这里举一个简单的例子,来演示spring
创建bean
过程中三级缓存的变化。假设需要往spring
容器中添加两个对象A、B
,且这两个对象相互依赖,且spring
先处理A
在处理B
。
简单通过文字描述下整个流程(当然了三级缓存中的一级缓存中的创建完成的bean
不止这两个对象,还包含spring
内置的一些对象):
A对象的创建过程
A对象实例化:这时仅通过A对象的无参构造函数,创建了一个空对象。
将A提前暴露的工厂添加到第三级缓存中:通过addSingletonFactory()
方法,这个方法的第二个参数,是个函数式接口,核心逻辑就是使得在第一步实例化的这个空对象提前被满足条件的BeanPostProcessor
处理后暴露出来。(所以经过这一步骤后三级缓存中的第三级缓存工厂中添加了一个提交暴露A对象的工厂方法。)
A对象依赖注入:假设A对象需要引入的B对象是通过@Autowird
属性注入的方式注入的,那么这时会进入到AutowiredAnnotationBeanPostProcessor#postProcessProperties
进行属性遍历解析后注入,而在这个时间节点,B对象还未创建,所以进入B对象的创建流程。
B对象的创建过程
3.1 B对象实例化:这时仅通过B对象的无参构造函数创建了一个空的B对象。
3.2 添加B对象的提前暴露工厂:同理步骤2。(所以经过这一步骤后三级缓存中的第三级缓存工厂中又添加了一个提交暴露B对象的工厂方法。在这个时间节点,这个工厂列表中就有两个元素)。
3.3 B对象依赖注入:同理步骤3的前半步骤。由于这个时候去三级缓存中的一级和二级缓存中获取不到A对象,则进入第三级缓存,通过第三级缓存,获取到了A对象的提前暴露对象,同时移除第三级缓存中A的工厂对象。(这时第二级缓存中存在一个A的提前暴露对象,第三级缓存中仅剩B对象的提前暴露工厂)
3.4 B对象的初始化:执行一些指定初始化方法,aware
方法等。(这时第一级缓存中就有一个创建完成的B对象,第二级缓存中存在一个A的提前暴露对象,第三级缓存中无元素) ==>准确的说这个第一级缓存的添加B对象和第三级缓存中的移除提前暴露B对象的工厂操作并不是在初始化(initializeBean
)方法中,而是在获取到B对象之后,有一个是否创建了实例的校验。具体代码在DefaultSingletonBeanRegistry#getSingleton
方法的finally
之后,有兴趣可以自行查看。
这时回到了A对象的注入流程,而A对象就可以获取到创建好的B对象,将B通过反射注入到自身属性中。(所以经过这一步骤后第二级缓存中存在一个A的提前暴露对象,第三级缓存中仅剩B对象的提前暴露工厂)
最后A对象的实例化:与3.4步骤同理。最后三级缓存中仅有一级缓存中存在两个元素。
小结: 其实分析下来,整个过程并不难理解,提前暴露到二级缓存中的空对象,其实是每个bean
创建的第一步,也就是每个bean
都首先会经过一次创建空对象的过程,然后将这个空对象是否需要暴露的决定权,添加到第三级缓存的工厂对象中,如果出现其他对象引用的时候,则提交暴露这个空对象,让依赖对象主体持有这个空对象的引用,这样并不会影响到被依赖对象后续的注入与初始化。
上述的创建流程是最理想的情况,而比较复杂的情况是3.3步骤中,通过第三级缓存中获取到A对象,有可能并不是在步骤1中创建的空对象 ,而是经过一些BeanPostProcessor
处理过的代理对象,比如AOP
代理。
在看两个个例子:
A与B分别通过@Autowired
注入对方,且B被LogAspect
进行AOP
处理。这种情况下,在3.3步骤B去获取A的提前暴露对象后,B在初始化方法中经过AOP
变成一个代理对象返回给A对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Service public class A { @Autowired private B b; } @Service public class B { @Autowired private A a; public void printHelloWorld () { } } @Slf4j @Aspect @Component public class LogAspect { @Pointcut("execution(public * cn.com.xiaocainiaoya.cyclic.B.printHelloWorld(..))") private void testPointcut () {} @Around("testPointcut()") public void around (ProceedingJoinPoint point) throws Throwable { log.info("LogAspect before" ); point.proceed(); log.info("LogAspect after" ); } }
A与B分别通过@Autowired
注入对方,且A被LogAspect
进行AOP
处理。这种情况下,在3.3步骤B去获取A的提前暴露对象时,获取的是一个代理对象,B将这个代理对象的引用挂在自身声明的A属性处后,将B对象返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Service public class A { @Autowired private B b; public void printHelloWorld () { } } @Service public class B { @Autowired private A a; } @Slf4j @Aspect @Component public class LogAspect { @Pointcut("execution(public * cn.com.xiaocainiaoya.cyclic.A.printHelloWorld(..))") private void testPointcut () {} @Around("testPointcut()") public void around (ProceedingJoinPoint point) throws Throwable { log.info("LogAspect before" ); point.proceed(); log.info("LogAspect after" ); } }
判定规则 其实判定是否是循环依赖报错并不是在上面说的创建过程三个步骤中,而是在每一个对象经过这三个步骤后,要添加到spring
容器中之前有一个判断逻辑,来进行判定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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 " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example." ); } } } }