关于java:Autowired如何解决循环依赖

52次阅读

共计 4568 个字符,预计需要花费 12 分钟才能阅读完成。

在 spring 框架下,咱们能够通过 @Autowired 注解对属性或者办法参数进行标注,当 spring ioc 容器初始化时,会帮咱们从容器中拿到对应的实例进行注入

什么是循环依赖

如果当初有两个 Bean 如下所示

public class BeanA {
    @Autowired
    private BeanB beanB;
}

public class BeanB {
    @AutowiredgetSingleton
    private BeanA beanA;
}

而后咱们通过 annotationConfigApplicationContext#register 将两个 bean 的信息注入到容器中,最初通过 refresh 进行容器到初始化操作

public static void main(String[] args) {

    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(Bean1.class);
    annotationConfigApplicationContext.register(Bean2.class);
    annotationConfigApplicationContext.refresh();}

能够看到 A 跟 B 相互依赖,试着设想:当容器先初始化 beanA 时,必然要对属性 beanB 进行赋值,这个时候容器中还没有 beanB,那么势必会触发 beanB 的初始化流程,而 beanB 初始化的实现也须要对属性 beanA 赋值,但 beanA 还未初始化实现,这里就产生了所谓的循环依赖。

spring 如何解决循环依赖

这里有一个很要害的属性:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

key 是 beanName,value 是一个对象工厂,咱们点进去看一下

public interface ObjectFactory<T> {T getObject() throws BeansException;
}

其实这里的 getObject()就是最终解决循环依赖所调用的办法。
那么程序是怎么执行到这的呢?
咱们先从 bean 的创立动手
如果容器还未实例化 bean,那么就会走到这里

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (instanceWrapper == null) {
            // 实例化 bean,如果 @Autowired 加在构造方法上,// 那么就会在这里实现注入
            // 因为上面的回调还未注册,所以这里无奈解决循环依赖
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        
        final Object bean = instanceWrapper.getWrappedInstance();
        
        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");
            }
            // 往单例工厂 (之前说的 singletonFactories) 中增加一个
            //ObjectFactory 的匿名实现作为回调,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            
            // 属性赋值,解决 @Autowired(非构造方法)
            populateBean(beanName, mbd, instanceWrapper);
        }

这里咱们发现,在实例化 bean 跟对属性赋值之间有一个 addSingletonFactory 的操作,作用是注册一个能够获取以后正在创立的 bean 的一个回调

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);
            }
        }
    }

进入回调,发现回调默认返回的就是 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;
    }
    
    default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        //    返回 bean 自身
        return bean;
    }

ok,这里得出一个论断,即便 bean 未初始化实现,spring 也提供了办法来获取这个 bean 的实例。
如果利用到咱们下面的栗子中来就是:

beanA 实例化实现
增加获取 beanA 的回调到 singletonFactories
调用 populateBean,解决 @Autowired,注入 beanB
因为 beanB 还未创立,那么势必会进入创立 beanB 的流程,当 beanB 也走到 populateBean 时,也须要实现 beanA 的注入,这时就会尝试从 beanFactory 中获取 beanA,这里最终会进到
AbstractBeanFactory 的 doGetBean 中

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);
    }

这里很要害,进入 getSingleton(beanName)

    public Object getSingleton(String beanName) {return getSingleton(beanName, true);
    }
    
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 先从一级缓存中查找
        Object singletonObject = this.singletonObjects.get(beanName);
        // 如果一级缓存中没有,且以后 bean 正处于创立的过程中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {
                 // 从二级缓存中查找
                singletonObject = this.earlySingletonObjects.get(beanName);
                 // 如果二级缓存中也没有,且容许裸露晚期援用时
                if (singletonObject == null && allowEarlyReference) {
                    // 从三级缓存中查找到 bean 的工厂
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 调用 getObject 办法生成 bean
                        singletonObject = singletonFactory.getObject();
                        // 放入到二级缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 从三级缓存中移除
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

当 beanB 走到这里时通过 beanA 的 beanName 获取 beanA,首先会尝试从 singletonObjects 中获取,这里必定获取不到,因为 singletonObjects 的 put 操作是在 bean 初始化实现之后。所以只能通过调用之前注册的回调 singletonFactory.getObject()来获取 beanA。
那么到此 beanA 注入到 beanB 的顺利完成,当 beanB 初始化实现之后,其实 beanA 的 getBean()也就返回了 beanB 的援用,到此 beanA 也能够顺利完成依赖注入。

正文完
 0