循环依赖
什么是Spring循环依赖
字面意思就是A依赖B的同时B也依赖于A。
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
什么情况下循环依赖可以被处理?
Spring解决循环依赖是有前置条件:
- 出现循环依赖的Bean必须要是单例
- 依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)
Spring是如何解决的循环依赖?
简单的循环依赖(没有AOP)
以上方简单代码A,B为例,Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建A。
Spring在创建Bean的过程中分为三步:
-
实例化,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法
-
属性注入,对应方法:AbstractAutowireCapableBeanFactory的populateBean方法
-
初始化,对应方法:AbstractAutowireCapableBeanFactory的initializeBean
Debug代码流程
ClassPathXmlApplicationContext处断点
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = context.getBean("a", A.class);
B b = context.getBean("b", B.class);
}
此处调用getBean创建一个新的Bean。
创建A的过程实际上就是调用getBean方法,这个方法有两层含义
- 创建一个新的Bean
- 从缓存中获取到已经被创建的对象
尝试从各级缓存获取A的实例bean
此处利用getSingleton方法重载,开始创建bean实例
此处调用createBean方法,里面有个doCreateBean开始创建Bean实例。
此处使用createBeanInstance创建Bean实例。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
Class<?> beanClass = this.resolveBeanClass(mbd, beanName, new Class[0]);
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());
} else {
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return this.obtainFromSupplier(instanceSupplier, beanName);
} else if (mbd.getFactoryMethodName() != null) {
return this.instantiateUsingFactoryMethod(beanName, mbd, args);
} else {
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized(mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
return autowireNecessary ? this.autowireConstructor(beanName, mbd, (Constructor[])null, (Object[])null) : this.instantiateBean(beanName, mbd);
} else {
Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors == null && mbd.getResolvedAutowireMode() != 3 && !mbd.hasConstructorArgumentValues() && ObjectUtils.isEmpty(args)) {
ctors = mbd.getPreferredConstructors();
return ctors != null ? this.autowireConstructor(beanName, mbd, ctors, (Object[])null) : this.instantiateBean(beanName, mbd);
} else {
return this.autowireConstructor(beanName, mbd, ctors, args);
}
}
}
}
前面完成了创建Bean实例,接下来调用addSingletonFactory方法,将beanA添加到三级缓存当中。
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);
}
}
}
这里只是添加了一个工厂,通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象,而这个对象实际上就是通过getEarlyBeanReference这个方法创建的。那么,什么时候会去调用这个工厂的getObject方法呢?这个时候就要到创建B的流程了。
此时开始对A进行属性注入,在注入的时候发现了A依赖于B,这个时候Spring又会去getBean(b),然后反射调用setter方法完成属性注入。
此处调用getBean给B创建一个新的Bean。
利用getSingleton(beanName)给B创建Bean。此时A实例化并添加进了三级缓存,并不在一级缓存中。重复上面的步骤,将B实例化并添加到三级缓存中,
此时需要给B填充属性。
给B填充属性的过程中,调用getBean(a),此处的getBean(a)与刚开始的不同,刚开始是创建Bean,此处是从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中。
此处将放在三级缓存中的A实例放进到二级缓存当中去。
此处引用别人博客中getEarlyBeanReference的解释。
这里我们可以看出,注入到B中的A是通过getEarlyBeanReference方法提前暴露出去的一个对象,还不是一个完整的Bean,那么getEarlyBeanReference到底干了啥了,我们看下它的源码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
Iterator var5 = this.getBeanPostProcessors().iterator();
while(var5.hasNext()) {
BeanPostProcessor bp = (BeanPostProcessor)var5.next();
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator。也就是说如果在不考虑AOP的情况下,上面的代码等价于:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
return exposedObject;
}
也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP的情况下三级缓存有用嘛?讲道理,真的没什么用,我直接将这个对象放到二级缓存中不是一点问题都没有吗?如果你说它提高了效率,那你告诉我提高的效率在哪?
接着上面讲A的实例从三级缓存放入二级缓存中,回到创建B实例的过程中去,将B的实例化流程走完,此时从缓存中获取B,B不在一级缓存中,返回null,
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
此时调用addSingleton方法讲B实例添加到一级缓存中去。接着去走A的流程。
接着完成A的实例化,
此时在二级缓存中找到A的实例
调用addSingleton方法讲A添加到一级缓存中去。完成A的实例化过程。
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);
}
}
小总结:
图解:
文字:
1.首先是调用getBean()方法,底层调用了doGetBean()方法,调用了getSingleton()方法从缓存中查找BeanA。
2.因为还没实例化,所以缓存中目前没有,调用getSingleton()的重载方法。
3.在getSingleton()方法中,将这个beanA的名字添加到一个集合当中(singletonsCurrentlyInCreation),标记该BeanA正在创建当中,然后回调匿名内部类的createBean()方法。
4.走到doCreateBean()方法当中,先利用反射调用构造器创建出BeanA的实例,后面会判断是否为单例(true),是否允许提前暴露引用,是否是正在创建当中(即是否在第三步的集合当中),都正确将BeanA添加到三级缓存中。
5.之后就是调用populateBean()对BeanA进行属性填充,检测到A依赖于B,就去查找B。
6.和上述查找A的方式相同,查找B,发现B又依赖于A。
7.之后就是调用getSingleton()方法去获取BeanA。依次从一级,二级,三级缓存中找Bean A,在三级缓存中找到BeanA的创建工厂,可以通过singletonFactory获取到singletonObject,singletonObject就是上面通过doCreateBean()方法实例的Bean A,此时BeanB找到BeanA的实例,将BeanA从三级缓存中移动到二级缓存中,然后调用addSingleton()方法将BeanB完直接添加到一级缓存中,完成BeanB的实例化过程。
8.最后又是BeanA继续填充属性,可以从一级缓存中获取到BeanB的实例,最后通过addSingleton()方法将BeanA从二级缓存移动到一级缓存。从而BeanA也就完成创建。
结合了AOP的循环依赖
下方是没有结合AOP的时候,通过singletonFactory.getObject();调用的getEarlyBeanReference方法。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
Iterator var5 = this.getBeanPostProcessors().iterator();
while(var5.hasNext()) {
BeanPostProcessor bp = (BeanPostProcessor)var5.next();
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
如果在开启AOP的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference方法,对应的源码如下:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
回到上面的例子,我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。
看到这个图你可能会产生下面这些疑问
在给B注入的时候为什么要注入一个代理对象?
答:当我们对A进行了AOP代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象。
所有断点:
为什么Sping不选择二级缓存方式,而是要额外加一层缓存?
如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。