0%

Spring循环依赖(上)

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中分别对应的是instantiationpopulateinitialization。在没有看spring之前,我一度以为实例化和初始化是一个意思,一直以为两个单词是一个意思。

  • 实例化(instantiation):通过指定的静态函数或者实例化对象的某个函数,或者是无参构造函数等方式创建一个空对象。
  • 注入依赖项(populate):比如实现了BeanPostProcessorInstantiationAwareBeanPostProcessor开放的扩展点进行特定情况的依赖项注入,比如在属性上标记的@Autowird,则是通过AutowiredAnnotationBeanPostProcessor进行注入处理。
  • 初始化(initilization):注入依赖项之后的一些操作,比如指定了初始化initMethod方法,或者是BeanPostProcessor#postProcessBeforeInitialization接口等。

Springbean的创建过程,主要是从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) {
// Prepare this context for refreshing.
// 准备刷新容器操作(设置一些状态位,监听器、事件器)
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
// 实例化一个Bean工厂(DefaultListableBeanFactory)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
// 初始化BeanFactory, 进行一些属性的初始化赋值
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
// 空方法扩展点(springboot中有使用到)
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
// 调用BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
// 注册所有的BeanPostProcessor
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
// 国际化
initMessageSource();

// Initialize event multicaster for this context.
// 初始化事件广播器
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
// 扩展点, 由子类实现
onRefresh();

// Check for listener beans and register them.
// 注册事件监听器
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
// 实例化所有的非懒加载的单例对象
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
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);
}
// 篇幅原因省略部分代码
... ...

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// bean是否需要提前暴露: 需满足三个条件 -> 1.是单例; 2.支持循环依赖; 3.bean正在创建中(在singletonsCurrentlyInCreation中能获取到)
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 {
// ②填充属性
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) {
// 1.获取这个bean的class属性,确保beanDefinition中beanClass属性已经完成解析
// Make sure bean class is actually resolved at this point.
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());
}

// 2.通过beanDefinition中的supplier实例化这个bean,一般情况下不会通过这种方式创建
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}

// 3.通过FactoryMethod实例化这个bean
// 通过工厂方式实例化(包含静态工厂和实例工厂)
// @Bean是通过实例工厂(实例工厂也被spring容器管理[获取对应Bean之后通过反射调用实例化对象])来实例化对象
// xml方式中可以指定class为静态类factory-method为静态方法
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}

// 4.下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,一种是通过推断出来的构造函数
// Shortcut when re-creating the same bean...
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);
}
}

// Candidate constructors for autowiring? 获取构造函数
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}

// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}

// No special handling: simply use no-arg constructor.
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) {
// 篇幅原因省略部分代码
... ...

// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 是否允许注入校验, 如果postProcessAfterInstantiation返回false则表示不允许注入
// 直接跳出populateBean方法
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;
// 执行InstantiationAwareBeanPostProcessor#postProcessProperties
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 {
// 1. 调用aware扩展点方法
invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 2. 执行BeanPostProcessor#postProcessBeforeInitialization
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
// 3. 执行指定的初始化initMethod方法
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()) {
// 4. 执行BeanPostProcessor#postProcessAfterInitialization
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
// bean是否需要提前暴露: 需满足三个条件 -> 1.是单例; 2.支持循环依赖; 3.bean正在创建中
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));
}

//DefaultSingletonBeanRegistry#addSingletonFactory
// 这里会将这个实例化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);
}
}
}

// 实例化bean的工厂方法
// 这个方法大致的作用就是对传入的这个bean对象, 进行满足一下条件的BeanPostProcessor的处理
// 默认情况下只有AOP的一个BeanPostProcessor。在目标bean无需AOP的情况下, 返回的其实就是传入的bean
// 也就是仅仅只经过实例化的bean。
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对象的创建过程

  1. A对象实例化:这时仅通过A对象的无参构造函数,创建了一个空对象。

  2. 将A提前暴露的工厂添加到第三级缓存中:通过addSingletonFactory()方法,这个方法的第二个参数,是个函数式接口,核心逻辑就是使得在第一步实例化的这个空对象提前被满足条件的BeanPostProcessor处理后暴露出来。(所以经过这一步骤后三级缓存中的第三级缓存工厂中添加了一个提交暴露A对象的工厂方法。)

  3. 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对象的提前暴露工厂)

  4. 最后A对象的实例化:与3.4步骤同理。最后三级缓存中仅有一级缓存中存在两个元素。

小结:其实分析下来,整个过程并不难理解,提前暴露到二级缓存中的空对象,其实是每个bean创建的第一步,也就是每个bean都首先会经过一次创建空对象的过程,然后将这个空对象是否需要暴露的决定权,添加到第三级缓存的工厂对象中,如果出现其他对象引用的时候,则提交暴露这个空对象,让依赖对象主体持有这个空对象的引用,这样并不会影响到被依赖对象后续的注入与初始化。

上述的创建流程是最理想的情况,而比较复杂的情况是3.3步骤中,通过第三级缓存中获取到A对象,有可能并不是在步骤1中创建的空对象 ,而是经过一些BeanPostProcessor处理过的代理对象,比如AOP代理。

在看两个个例子:

  1. 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");
}
}
  1. 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
//AbstractAutowireCapableBeanFactory#doCreateBean

if (earlySingletonExposure) {
// 从三级缓存中获取提前暴露对象
Object earlySingletonReference = getSingleton(beanName, false);
// 若存在, 有可能是一级缓存也有可能是二级缓存
if (earlySingletonReference != null) {
// 比较经过实例化创建的bean和经过初始化之后的exposeObject是否相等
if (exposedObject == bean) {
// 若相等 --> 将提前暴露的对象注入到容器中
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// 判断这个beanName对应的bean的依赖是否都创建完成
// 若依赖都创建完成, 则将初始化的exposedObject注入到容器中
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.");
}
}
}
}
-------------本文结束感谢您的阅读-------------