循环依赖
循环依赖如下图所示:
对应的spring代码形式如下:
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private C c;
}
@Component
public class C {
@Autowired
private A a;
}
又或者: 对应的spring代码形式如下:
@Component
public class A {
@Autowired
private A a;
}
上面展示的循环依赖都是Spring可以解决的,但是对于构造器的循环依赖注入,Spring无法解决,会抛出异常:
@Component
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
对于Prototype类型的bean来说,如果存在循环引用也是会直接抛出异常结束
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class A {
@Autowired
private B b;
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class B {
@Autowired
private A a;
}
如何解决循环依赖
在正式研究Spring如何解决循环依赖之前,不如我们就假设spring目前没有提供三级缓存来解决循环依赖,那么目前spring的getBean流程图就如下所示:
getBean总共就三个大的阶段:
对于Spring而言,循环依赖会发生在第二步,即属性注入的过程,因此我们要想办法在属性注入前将当前Bean依赖的其他bean都进行创建。
在属性注入环节,如果发现当前bean依赖于其他bean,那么会去创建对应的bean.
不妨思考下面这个流程:
大家可以思考一下,如何解决上面这个死循环.
这里提前暴露的意思就是将已经实例化bean但是还没有赋值的bean实例放到一个缓存池中,可以让其他bean在需要当前bean,但是当前bean还没初始化完的情况下,先引用这个暴露的bean
注意: 暴露出去的是这个bean对象的引用,因此该bean后续的属性注入的修改都是在同一块内存上完成的。
添加了提前暴露的缓存池后,循环引用的流程就变成了下面这样子:
还有一点大家可以看出来,因为必须在实例化后,当前bean对象才会被提前放入缓存池中,因此构造器造成的循环依赖无法解决
可以看到此时循环引用的问题就已经被解决了,但是我们给出的解决方案还存在诸多问题,但是思路是正确的,那么下面来看看Spring是如何完美解决bean的循环依赖的吧。
三级缓存解决循环依赖
我们上面只使用了二级缓存,即一个单例缓存池和一个提前暴露的单例缓存池,但是spring在此基础上多加了一个缓存池,并且具体的使用上也和我们上面讲的有点区别:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
...
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}
三级缓存的作用分别如下:
下面跟随我的脚本一起来看看getBean过程中三级缓存是如何发挥作用并巧妙解决循环依赖问题的吧:
三级缓存解决流程
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
...
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {...}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
....
}
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
....
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
getMergedLocalBeanDefinition方法虽然与本文主题没多大关系,但是因为这个方法还是蛮有用的,我还是忍不住想拿出来分析一下:
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
获取当前beanName对应的解析好的beanDefinition
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
BeanDefinition bd = this.beanDefinitionMap.get(beanName);
if (bd == null) {
if (logger.isTraceEnabled()) {
logger.trace("No bean named '" + beanName + "' found in " + this);
}
throw new NoSuchBeanDefinitionException(beanName);
}
return bd;
}
我们通过registerBeanDefinition方法注册进来的bean定义,都是放入了beanDefinitionMap集合中,这点需要明白
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
throws BeanDefinitionStoreException {
return getMergedBeanDefinition(beanName, bd, null);
}
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
RootBeanDefinition previous = null;
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
if (mbd == null || mbd.stale) {
previous = mbd;
if (bd.getParentName() == null) {
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
mbd = new RootBeanDefinition(bd);
}
}
else {
BeanDefinition pbd;
try {
String parentBeanName = transformedBeanName(bd.getParentName());
if (!beanName.equals(parentBeanName)) {
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without a ConfigurableBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);
}
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(SCOPE_SINGLETON);
}
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
if (containingBd == null && isCacheBeanMetadata()) {
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
if (previous != null) {
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
}
return mbd;
}
}
之所以带大家看看getMergedBeanDefinition方法的实现,原因在于让各位明白,我们通过registerBeanDefinition方法注册进来的BeanDefinition是存放在beanDefinitionMap集合中。
而最终如果我们想要获取到当前bean的实例,还需要从beanDefinitionMap取出对应的BeanDefinition,然后通过getMergedBeanDefinition转换为合并后的RootBeanDefinition,同时会放入mergedBeanDefinitionHolders集合中去
我们最关心的应该是三层缓存的查询方法,即getSingleton:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && 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 = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
我相信各位此时都有一个疑惑,为什么需要singletonFactories,而不是直接两级缓存完事了呢?
先来看看getSingleton方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
下面再来看看createBean方法:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
RootBeanDefinition mbdToUse = mbd;
...
try {
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
....
}
看来还需要再追踪一层doCreateBean:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
...
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
....
}
}
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
...
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
....
}
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.");
}
}
}
}
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
...
return exposedObject;
}
从上面代码的分析中,我们洞悉到了一点:
模拟流程
这里我们可以模拟一下Spring三级缓存解决循环依赖的过程:
aop与循环依赖
我相信各位还记得getEarlyBeanReference 方法,也就是从三级缓存中取出提早暴露的bean时,会调用该方法,那么这与aop有什么关系呢?
getEarlyBeanReference实际会去调用相关后置处理器的回调接口来对bean进行处理,而不是单单返回原本的bean实例:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
通过追踪getEarlyBeanReference方法的实现子类,我们可以发现只有AbstractAutoProxyCreator实现了此方法,即抽象的自动代理创建器实现了这个方法:
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
wrapIfNecessary决定当前被提早暴露的bean是否需要被代理,那么判断条件是什么呢?
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
可以看到getEarlyBeanReference接口可以确保被提前暴露的bean,如果是需要被代理的,那么直接通过这里的回调接口返回代理对象,这样在存在循环依赖的情况下,注入的对象也是代理对象,而非原对象。
postProcessAfterInitialization: bean的初始化方法被调用后执行
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这里可能有小伙伴会疑问,如果getEarlyBeanReference()被调用后返回的是代理后的对象,那么postProcessAfterInitialization()方法的返回的是原始bean对象,这不是对不上了吗?
先来看看存在循环依赖并且自动代理创建器将提前暴露的bean给代理了的情况:
如果只有自动代理创建器的情况下,由于存在循环依赖getEarlyBeanReference方法返回的是代理对象,那么postProcessAfterInitialization就会返回原对象,因此exposedObject和bean是相等的,所以会将提前暴露的bean赋值给exposedObject对象
当然没问题,之所以会有这个疑惑,是因为对动态代理底层不够了解,cglib动态代理和jdk动态代理在生产代理对象的时候,是需要获取到目标对象引用的,这个引用和后面被经过属性赋值和初始化阶段的bean引用是同一个,然后再方法被调用的时候,是会去调用内部保存的目标对象方法的,因此最终访问的是同一个对象,没得问题。
代理对象底层的拥有的目标对象和原始bean的引用是一致的,这点要牢记,不然容易搞迷糊
|