由于md文档部分hugo插件语法不兼容,建议访问作者网站查阅文章:wlizhi.cc
spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework 。
链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。
1 什么是循环依赖
在我们日常开发中,肯定存在这种情况:bean A 某个成员是 bean B,bean B 中某个属性是 bean A。那A类和B类就是相互依赖的关系,也叫循环依赖。
当然,也可以是这种情况 A -> B -> C -> D -> E -> A。这种关系也是循环依赖。
2 循环依赖在spring中的实现原理
{{< highlight java “linenos=table,hl_lines=,linenostart=1” >}}
@Service
public class CyclicGoodsServiceImpl implements CyclicGoodsService {
@Autowired
private CyclicOrderService cyclicOrderService;
@Override
public CyclicOrderService getCyclicOrderService() {
return cyclicOrderService;
}
}
{{< / highlight >}}
2.1 源码分析
在这之前,先看一个简单的循环依赖代码案例:
{{< highlight java “linenos=table,hl_lines=3 4 14 15,linenostart=1” >}}
@Service
public class CyclicGoodsServiceImpl implements CyclicGoodsService {
@Autowired
private CyclicOrderService cyclicOrderService;
@Override
public CyclicOrderService getCyclicOrderService() {
return cyclicOrderService;
}
}
@Service
public class CyclicOrderServiceImpl implements CyclicOrderService {
@Autowired
private CyclicGoodsService cyclicGoodsService;
@Override
public CyclicGoodsService getCyclicGoodsService() {
return cyclicGoodsService;
}
}
@Slf4j
public class ApplicationContextTest {
@Test
public void testCyclicDependence(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(“top.wlz922.cyclic”);
CyclicGoodsService goodsService = context.getBean(CyclicGoodsService.class);
CyclicOrderService orderService = context.getBean(CyclicOrderService.class);
log.debug(goodsService.toString());
log.debug(orderService.toString());
}
}
{{< / highlight >}}
只是简单的打印一下两个Service类中属性的值,很明显是注入成功了。
以下是打印的内容:
19:34:42.334 [main] DEBUG top.wlz922.test.context.ApplicationContextTest 36 - top.wlz922.cyclic.CyclicGoodsServiceImpl@7fa98a66
19:34:42.335 [main] DEBUG top.wlz922.test.context.ApplicationContextTest 37 - top.wlz922.cyclic.CyclicOrderServiceImpl@15ff3e9e
前面的文章中已经分析过,spring中获取bean实例的方法是getBean()。在容器启动的时候,会调用这些方法,实例化单例bean实例。
直接来到doGetBean(),看以下代码:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//转换beanName,这里传入进来的beanName可能是以&开头的,这里会截掉&
final String beanName = transformedBeanName(name);
Object bean;
/*
TODO 这里多级缓存用于解决循环依赖问题。
注:循环依赖只能是属性见的依赖,不能是构造函数中参数的循环依赖。
从缓存(一级二级三级缓存依次获取,获取到就返回)中获取单例实例,如果获取到并且是无参构造函数,则将bean变量的值赋值。
*/
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 省略无关代码
// 如果bean不是FactoryBean类型或者beanName以&开头,则直接返回。
// 否则将bean强转为FactoryBean类型,并调用getObject()方法返回。
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 省略无关代码...
return (T) bean;
}
}
先跳过无关代码,看关键点。从上面代码中可以看出,在doGetBean()中先调用了getSingleton(),如果这个值不为空就调用getObjectForBeanInstance(),经过一定的处理(先不要关心具体处理了什么),返回这个bean实例。
可以先分析,从接收变量的变量名sharedInstance就可以看出来,这是一个bean实例。关键点就在getSingleton()方法,这里可能会获取到一个bean实例。
下面是 getSingleton() 源码:
{{< highlight java “linenos=table,hl_lines=25 28 30,linenostart=1” >}}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** 一级缓存 里面存储的是bean实例,在bean实例化完成、依赖注入、代理生成等流程全部完成之后,会存储到这个容器中 /
/* Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 三级缓存,在单例bean刚刚创建完成时,会将其封装成一个ObjectFactory对象,存储到这个容器中。键值是beanName。
* 从源码中可以看到,实际上是提供了一些通过BeanPostProcessor进行的扩展操作。这里不会执行,只是把这个扩展操作流程封装起来。
* 实际上,这里封装的一个函数式接口,在这里可以通过BeanPostProcessor进行一些扩展操作,其节点是bean实例化之后,
* 在真正getBean时,会触发调用。 */
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* 二级缓存 从三级缓存获取后,会将bean实例存储到二级缓存,并从三级缓存中移除。这里封装了一个函数式接口的匿名实例,
* 依赖注入过程中通过getBean方法获取这个实例时,可以对其进行一些指定操作,比如说aop代理对象生成,就可能在这里触发。
*/
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
/**
* 从一级缓存中取,如果有值,则返回。否则依次查找二级、三级缓存,如果最终从三级缓存中拿到值,
* 则将bean对象升级到二级缓存,并把原值从三级缓存中移除。
*/
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}
{{< /highlight >}}
从源码添加的中文注释中可以看出,BeanFactory有这么三个关键成员。分别是一、二、三级缓存。
一级缓存 singletonObjects:封装了最终完成了所有处理流程的Bean。
二级缓存 earlySingletonObjects:封装了通过ObjectFactory.getObject()处理过后的对象。
三级缓存 singletonFactories:Bean刚刚创建完实例后,将Bean实例封装成ObjectFactory对象,存储到这里。
暂且记着这三个缓存的作用,来找bean被存储到三级缓存的源头。
在一个bean刚刚创建的时候,会调用到doCreateBean方法,在这个方法中真正实例化了bean,那么实例化完成之后,紧接着就会将其包装成ObjectFactory对象,存储到三级缓存。来到这块代码:
{{< highlight java “linenos=table,hl_lines=8 22,linenostart=1” >}}
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 省略无关代码…
// 创建bean实例
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
// 省略代码:对一些注解支持的扫描、合并到BeanDefinition…
// 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实例的引用,这时候的bean是刚刚创建完成的,还没有进行属性的依赖注入、等流程处理。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
}
}
{{< /highlight >}}
从上面列出的spring源码中可以看到,当一个bean创建完实例后,会调用addSingletonFactory(),这个方法的第二个参数,传递了一个匿名对象,这个对象就是ObjectFactory类型的。
来到addSingletonFactory()源码:
{{< highlight java “linenos=table,hl_lines=7,linenostart=1” >}}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, “Singleton factory must not be null”);
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 将这个bean的匿名对象,存储到三级缓存。匿名对象中封装了一个扩展点的操作。
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
}
{{< /highlight >}}
可以看到,在bean刚刚创建完成,就把其封装到了ObjectFactory中,存储到了三级缓存singletonFactories(一个Map)。
从调用这个方法的地方(addSingletonFactory()),可以看到,这个匿名函数的内容:() -> getEarlyBeanReference(beanName, mbd, bean)
来看一下这个匿名对象具体做了什么:
{{< highlight java “linenos=table,hl_lines=9 12,linenostart=1” >}}
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
// 获取早期的Bean对象的引用,这时候的Bean还没有完成依赖注入及其后面的流程。
// 在这里获取引用时,支持一些扩展操作,其扩展内容是通过 getEarlyBeanReference() 实现的。
// getEarlyBeanReference() 来自 BeanPostProcessor 的子接口 SmartInstantiationAwareBeanPostProcessor。
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
}
{{< /highlight >}}
从源码中可以看出:在返回早期暴露的Bean实例引用之前,会通过 BeanPostProcessor 进行一些处理。
处理内容:
{{< admonition type=“abstract” title=“摘要” open=true >}}
- 获取到容器中所有已注册的BeanPostProcessor。
- 判断是否是其子接口SmartInstantiationAwareBeanPostProcessor类型,如果是,强转。
- 调用 getEarlyBeanReference() 获取Bean提前暴露对象的引用,在这里可以对Bean实例做一些扩展操作(比如生成代理对象等)。
{{< /admonition >}}
在addSingletonFactory(),可以看到,这里只是将扩展操作封装起来,放入三级缓存,并没有调用它。
回到文章开头部分,依次从一、二、三级缓存中获取实例,在执行三级缓存获取操作时,就会调用这个方法,拿到Bean提前暴露的引用。拿到之后从三级缓存中移除,放入二级缓存。然后返回。
这也解释了为什么多个实例之间相互依赖,spring依然能够将依赖实例注入到对应成员中。
还有一个点,在哪里放入到一级缓存的?前面的文章中,有列出创建bean实例的入口,createBean方法是在一个函数式接口方法中调用的,它的上层方法是getSingleton()。
来到getSingleton()源码:
{{< highlight java “linenos=table,hl_lines=14,linenostart=1” >}}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
// 省略无关代码...
//这里调用了外层传进来的匿名内部类,lambda表达式。
singletonObject = singletonFactory.getObject();
newSingleton = true;
// 省略无关代码...
// 加入一级缓存
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
{{< /highlight >}}
singletonFactory.getObject() 中调用了createBean,这里完成了bean的实例化、依赖注入、代理生成等等操作。
在createBean所有操作完成之后,最后调用了addSingleton()方法。这里把bean实例加入到了一级缓存,并从二级缓存中移除。
来到addSingleton()源码:
{{< highlight java “linenos=table,hl_lines=,linenostart=1” >}}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
{{< /highlight >}}
很简单的逻辑,只是把一个对象放入到一级缓存,并从二级、三级缓存中移除。最后一个是已注册的单例bean容器,暂不讨论。
2.2 源码中要注意的细节
{{< admonition open=true >}}
构造函数中的依赖注入不可以循环依赖,多例依赖注入也不可以循环依赖。
-
构造函数不允许循环依赖:很容易明白,如果想要创建一个对象,比如调用它的构造函数,即使反射调用也是如此。如果A构造函数依赖B,B构造函数中依赖A,那么构造函数就无法调用完成,对象也就无从创建。永远也拿不到提前暴露的对象引用。
-
多例情况:不允许循环依赖,由于是多例,每次getBean都会创建新的实例,假如代码中定义了有循环依赖的属性,如果被允许,就会陷入死循环。
{{< /admonition >}}
多例情况,如果有循环依赖,会抛出异常,源码如下:
{{< highlight java “linenos=table,hl_lines=,linenostart=1” >}}
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// Fail if we’re already creating this bean instance:
// We’re assumably within a circular reference.
// 多例不支持循环引用。多例时,如果返回指定的原型bean是否正在创建中,则抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
}
{{< /highlight >}}
构造函数中如果有循环依赖,源码ConstructorResolver类中有这段代码,抛出异常:
{{< highlight java “linenos=table,hl_lines=,linenostart=1” >}}
try {
// 省略无关代码,如果这里有构造函数中的循环依赖,则抛出异常
}catch (BeansException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
}
{{< /highlight >}}
2.3 流程梳理
例如有A、B两个类进行实例化,分别将对方的实例注入到自身成员中,代码如下(前文中类似案例):
{{< highlight java “linenos=table,hl_lines=,linenostart=1” >}}
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
{{< /highlight >}}
分析上面代码的执行流程:
- A 类无参构造函数实例化后, 设置三级缓存。
- A 类 populateBean 进行依赖注入, 这里触发了属性 B 的 getBean 操作。
- B 类无参构造函数实例化后,设置三级缓存。
- B 类 populateBean 进行依赖注入,这里触发了属性 A 的 getBean 操作
- A 类之前正在实例化, singletonsCurrentlyInCreation 集合中有已经有这个 A 实例了,三级缓存里面也有了,所以这时候是从三级缓存中拿到的提前暴露的 A 实例,该实例还没有进行属性 B 的依赖注入,属性 B 为空。
- B 类拿到了 A 的提前暴露实例注入到属性 A 中了。
- B 类实例化已经完成,B 类的实例化是由 A 类实例化中属性 B 的依赖注入触发的 getBean 操作进行的,现在 B 已经实例化,所以 A 类中属性 B 就可以完成依赖注入了,这时候 A 类 B 属性已经有值了。
- B 类 A 属性指向的就是 A 类实例堆空间,所以这时候 B 类 A 属性也会有值了。