1 Star 0 Fork 1

wow / java-notes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
SpringBean循环依赖.md 27.75 KB
一键复制 编辑 原始数据 按行查看 历史
wow 提交于 2021-02-25 11:45 . 工程和笔记

概念

有三个类 A、B、C其中A 依赖B,B依赖C,C依赖A,这就形成了一个循环依赖。

20201030151812

循环依赖又分两种:

  • 构造器循环依赖:

    按照一般编程方式,此种依赖无解,但是Spring 提供了懒加载机制(创建代理对象 - @Lazy)来保证构造器循环依赖场景下,程序依然能够工作。

  • 属性循环依赖

    这种情况Spring采用了三个Map(earlySingletonObjects、singletonFactories、singletonObjects),来保证程序工作

    • Spring 只支持单例模式下的循环依赖,对于原型模式的循环依赖,只能够加上 @Lazy 修饰

构造器循环依赖

假设我们有以下代码:

BeanA

@Component
public class BeanA  {
    private BeanB b;

    //BeanA 在执行构造函数时依赖BeanB
    public BeanA(BeanB b) {
        this.b = b;
    }
}

BeanB

@Component
public class BeanB {
    private BeanA a;
    //BeanB构造时依赖BeanA ,但是构造参数使用@Lazy注解修饰
    public BeanB(@Lazy BeanA a) {
        this.a = a;
    }
}

经过执行我们发现Spring 并未抛出异常,接下来我们分析其原理。

  1. 当我们构造 BeanA 实例时,会进入下面的代码

    //AbstractAutowireCapableBeanFactory#createBeanInstance
    //获取 BeanA,的构造函数
    Constructor<?>[] ctors = 
            determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null 
        || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR 
        ||  mbd.hasConstructorArgumentValues() 
        || !ObjectUtils.isEmpty(args)) {
        
        return autowireConstructor(beanName, mbd, ctors, args);
    }
  2. 接着使用 ConstructorResolver类的autowireConstructor() 方法,根据构造器参数,从IoC容器中找到对应的实例。该类并不具备主动解析的能力,还是依赖AutowireCapableBeanFactory.resolveDependency 进行解析。

    protected BeanWrapper autowireConstructor(String beanName, 
                                            RootBeanDefinition mbd, 
                                            @Nullable Constructor<?>[] ctors,
                                            @Nullable Object[] explicitArgs) {
    
        return new ConstructorResolver(this)
            .autowireConstructor(beanName, mbd, ctors, explicitArgs);
    }

    需要注意的是,AutowireCapableBeanFactory.resolveDependency 的第一个入参是 new DependencyDescriptor(param, true),其中的true代表的含义是:该依赖项是强依赖,即required,如果找不到,会抛出NoSuchBeanDefinitionException。这也是Spring4以后官方推荐使用构造器注入的原因之一: 表明强依赖强系

    // org.springframework.beans.factory.support.
    //        ConstructorResolver#resolveAutowiredArgument
    
    protected Object resolveAutowiredArgument(
                            MethodParameter param,
                            String beanName,
                            @Nullable Set<String> autowiredBeanNames, 
                            TypeConverter typeConverter, 
                            boolean fallback) {
        // beanName = BeanA
        // paramType = BeanB.class
        Class<?> paramType = param.getParameterType();
        if (InjectionPoint.class.isAssignableFrom(paramType)) {
            InjectionPoint injectionPoint = currentInjectionPoint.get();
            if (injectionPoint == null) {
                throw new IllegalStateException(
                    "No current InjectionPoint available for " + param);
            }
            return injectionPoint;
        }
    
        // 走到熟悉的beanFactory.resolveDependency方法
        return this.beanFactory.resolveDependency(
                new DependencyDescriptor(param, true),
                beanName, autowiredBeanNames, typeConverter);
        // ... (省略)
    }
  3. 接着,便来到了熟悉的AutowireCapableBeanFactory#resolveDependency,此处会处理@Lazy注解,通过方法名getLazyResolutionProxyIfNecessary我们可以大胆猜测:使用代理的方式处理@Lazy

    // org.springframework.beans.factory.support.
    //          DefaultListableBeanFactory#resolveDependency 
    
        public Object resolveDependency(DependencyDescriptor descriptor,
                                        @Nullable String requestingBeanName, 
                                        @Nullable Set<String> 
                                        autowiredBeanNames, 
                                        @Nullable TypeConverter typeConverter) 
                                        throws BeansException {
    
            // ...(省略)
            // 处理@Lazy的Case
            Object result = getAutowireCandidateResolver().
                            getLazyResolutionProxyIfNecessary(
                                    descriptor, requestingBeanName);
            if (result == null) {
                result = doResolveDependency(descriptor, 
                                            requestingBeanName,
                                            autowiredBeanNames, 
                                            typeConverter);
            }
            return result;
    
        }
        ```
  4. 接着,先判断依赖项是否含有@Lazy注解,如果含有,通过buildLazyResolutionProxy方法生成代理对象返回

    // org.springframework.context.annotation.
    //  ContextAnnotationAutowireCandidateResolver
    //        #getLazyResolutionProxyIfNecessary
    
    public Object getLazyResolutionProxyIfNecessary(
                            DependencyDescriptor descriptor, 
                            @Nullable String beanName) {
        return (isLazy(descriptor) ? 
            buildLazyResolutionProxy(descriptor, beanName) : null);
    }
    
    // 判断依赖项里是否含有@Lazy注解
    protected boolean isLazy(DependencyDescriptor descriptor) {
        for (Annotation ann : descriptor.getAnnotations()) {
            Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
            if (lazy != null && lazy.value()) {
                return true;
            }
        }
        MethodParameter methodParam = descriptor.getMethodParameter();
        if (methodParam != null) {
            Method method = methodParam.getMethod();
            if (method == null || void.class == method.getReturnType()) {
                Lazy lazy = AnnotationUtils.getAnnotation(methodParam.
                                getAnnotatedElement(), Lazy.class);
                if (lazy != null && lazy.value()) {
                    return true;
                }
            }
        }
        return false;
    }
    // 对依赖项生成代理对象
    protected Object buildLazyResolutionProxy(final DependencyDescriptor 
                                            descriptor, 
                                            final @Nullable String beanName) {
        Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
                "BeanFactory needs to be a DefaultListableBeanFactory");
        final DefaultListableBeanFactory beanFactory = 
                            (DefaultListableBeanFactory) getBeanFactory();
    
        // 稍微记一下TargetSource[目标源],下文还要用到
        TargetSource ts = new TargetSource() {
            // ...(省略)
        };
        ProxyFactory pf = new ProxyFactory();
        pf.setTargetSource(ts);
        Class<?> dependencyType = descriptor.getDependencyType();
        if (dependencyType.isInterface()) {
            pf.addInterface(dependencyType);
        }
        // 通过ProxyFactory生成代理对象
        return pf.getProxy(beanFactory.getBeanClassLoader());
    }

    我们得到一个结论:通过在构造器参数中标识@Lazy注解,Spring 生成并返回了一个代理对象,因此给BeanA注入的BeanB并非真实对象而是其代理

    20201103090847

虽然构造器注入的循环依赖解决了,程序也能正常启动,但是程序执行的时候是不是我们想要的效果呢?也即是说,BeanA 中的代理对象 BeanB 如何与真实的 BeanB 对象关联起来的呢?

  1. 我们已经在 BeanB 中添加了一个方法 - log()

  2. 执行BeanA.BeanB.log方法,由于 BeanB 是个代理对象,必然先进入代理逻辑。由于 BeanB 并非接口,不能通过JDK代理,因此是通过Cglib代理,如下示:

    // org.springframework.aop.framework.CglibAopProxy.
    //      DynamicAdvisedInterceptor#intercept
    
    private static class DynamicAdvisedInterceptor implements 
                                    MethodInterceptor, Serializable {
    
        private final AdvisedSupport advised;
        
        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }
        
        @Override
        @Nullable
        public Object intercept(Object proxy, Method method, 
                                Object[] args, MethodProxy methodProxy) 
                                    throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            
            // 上文提到的TargetSource
            TargetSource targetSource = this.advised.getTargetSource();
            // Get as late as possible to minimize the time we "own" the 
            //      target, in case it comes from a pool...[可以仔细品品该注释]
            // 通过TargetSource来获取被代理的对象target
            target = targetSource.getTarget();
            
            // ...(省略)
            if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                
                Object[] argsToUse = AopProxyUtils
                        .adaptArgumentsIfNecessary(method, args);
                // 通过反射调用被代理对象的方法
                retVal = methodProxy.invoke(target, argsToUse);
            }
            // ...(省略)
        }
    }

    代码中,通过 TargetSource#getTarget 来获取被代理的对象 target,然后通过反射完成被代理对象的方法调用

    我们回过头来看此处的 TargetSource 是什么,在上面的 buildLazyResolutionProxy 方法中,构造了 TargetSource ,把省略的代码展开:

    TargetSource ts = new TargetSource() {
    @Override
    public Class<?> getTargetClass() {
        // Bar.class
        return descriptor.getDependencyType();
    }
    @Override
    public boolean isStatic() {
        return false;
    }
    @Override
    public Object getTarget() {
        // 通过beanFactory去真正解析依赖(Bar),将Spring IoC里真实的Bar返回
        Object target = beanFactory.doResolveDependency(
                descriptor, beanName, null, null);
        if (target == null) {
            Class<?> type = getTargetClass();
            if (Map.class == type) {
                return Collections.emptyMap();
            }
            else if (List.class == type) {
                return Collections.emptyList();
            }
            else if (Set.class == type || Collection.class == type) {
                return Collections.emptySet();
            }
            throw new NoSuchBeanDefinitionException(
                descriptor.getResolvableType(),
                "Optional dependency not present for lazy injection point");
            }
            return target;
        }
        @Override
        public void releaseTarget(Object target) {
        }
    };

    最关键的一行代码是Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);,它的作用是通过 beanFactory 去真正解析依赖 (BeanB),将 Spring IoC 里真实的 BeanB 返回,如此,就拿到了真正的BeanB 对象

    此处我们可以得到一个结论:代理对象 BeanB 与真实的 BeanB 对象,是通过 TargetSouce 关联起来的,每次执行被代理对象的方法时,都会先通过 TargetSouce 去拿到真实的对象DefaultListableBeanFactory.doResolveDependency,然后通过反射进行调用。

    想要查看 BeanA 的代理类 可以使用下面代码来查看 CGLib 的生成类

    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "文件夹路径");

构造器循环依赖总结

Spring 构造器注入循环依赖的解决方案是 @Lazy ,其基本思路是:对于强依赖的对象,一开始并不注入对象本身,而是注入其代理对象,以便顺利完成实例的构造,形成一个完成的对象,这样与其它应用层对象就不会形成互相依赖的关系;当需要调用真实对象的方法时,通过 TargetSouce,在容器中拿到真实的对象DefaultListableBeanFactory.doResolveDependency(),然后通过反射完成调用.

需要注意的是,在 BeanA 的构造阶段,并不能去调用 BeanB ,这是因为当前注入的是 BeanB 的代理对象,真实对象还没有生成,所以这时调用就会抛出异常,终止Spring 的加载。

Bean构造依赖及解决办法

流程图地址

https://gitmind.cn/app/flowchart/f2c901532

属性值循环依赖

假设我们有以下类

BeanA

@Component
public class BeanA {

    @Autowired
    private BeanB b;

    public BeanA() {
        System.out.println("init A ");
        System.out.println(this);
    }
}

BeanB

@Component
public class BeanB {

    @Autowired
    private BeanA a;

    public BeanB() {
        System.out.println("init B");
        System.out.println(this);
    }
}

Bean A、B 它们互相持有对方的引用,这就构成了循环依赖。

Spring 通过提前暴露创建中的单例对象,来解决这个问题。

为了实现提前暴露对象,Spring 使用了三个 Map 缓存创建以及创建中的对象,DefaultSingletonBeanRegistry:我们需要记住这三个Map


//单例Bean - 也就是完全创建好,并且初始化好的Bean
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = 
    new ConcurrentHashMap<String, Object>(256);

//单例工厂(也可能存放刚刚创建的单例对象,或者代理类)
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = 
    new HashMap<String, ObjectFactory<?>>(16);

//提前暴露的缓存Map 承接从工厂缓存的对象 ,
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = 
    new HashMap<String, Object>(16);

//单例对象,注册set - 当单例对象放到 singletonObjects 中时 记录该对象
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

接下来我们正式进入属性值循环依赖的解决:

  1. getBean(A) 开始,我们首先从缓存中加载 A

    //AbstractBeanFactory.doGetBean(...)
    //    Object sharedInstance = getSingleton(beanName);
    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //从 singletonObject (完整的单例对象集合)中取出单例对象
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                //从提前曝光的 singletonObject 集合取出对象
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    //从对象工厂 singletonFactories 取出对象
                    ObjectFactory<?> singletonFactory = 
                        this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //将对象工厂的对象转移到提前曝光的集合
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
  2. 由于上一步的并未有 Bean 返回,所以进入 Bean 的创建

    //AbstractBeanFactory.doGetBean(...)
    // Create bean instance.
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

    我们来看一下 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) {
                。。。。
                boolean newSingleton = false;
                。。。。
                try {
                    //传入的回调对象,使用它创建真实的单例
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
               。。。。
                if (newSingleton) {
                    //在这里我们将创建好的对象,放入到 单例缓存中
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }
  3. 查看 getSingleton 方法中的匿名函数 createBean(beanName, mbd, args);

    @Override
    protected Object createBean(String beanName, 
                                RootBeanDefinition mbd, 
                                @Nullable Object[] args)
                                        throws BeanCreationException {
        ......
        RootBeanDefinition mbdToUse = mbd;
        。。。。。
        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 {
    
        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            //从单例缓存中移除 实例
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //创建实例(如果对象使用@Lazy修饰,则返回代理)
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }
        ..........
    
        //将创建包的Bean放入Bean工厂缓存
        // 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");
            }
            //重点
            addSingletonFactory(beanName, () 
                -> getEarlyBeanReference(beanName, mbd, bean));
        }
    
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            //填充属性
            //执行Aware
            //执行BeanPostProcessor
            populateBean(beanName, mbd, instanceWrapper);
            //调用自定义的Init方法
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
       。。。。。。。。
       //返回
        return exposedObject;
    }

    populateBean

    protected void populateBean(String beanName, 
                                RootBeanDefinition mbd, 
                                @Nullable BeanWrapper bw) {
    
        ........
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME 
            || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            //通过getBean 获取 依赖
            // Add property values based on autowire by name if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }
            // Add property values based on autowire by type if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }
    
        .........
        if (pvs != null) {
            //依赖注入
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }

    随后回到 doCreateBean 方法 ,执行init 方法

    exposedObject = initializeBean(beanName, exposedObject, mbd);
  4. 接下来回到 getSingleton 方法

    addSingleton(beanName, singletonObject);

    将创建好的对象,放入单例缓存,并清除提现曝光缓存和工厂缓存

属性值循环依赖总结

111011313491_0字段循环依赖_1

如何判断循环依赖

我们上面说了 构造循环依赖原型属性循环依赖 的解决办法,那么 Spring 是如何检测到循环依赖呢?

检测原型模式下的属性循环依赖

  1. Spring 采用了,采用了 ThreadLocal 来保存正在创建的对象, 当我们创建对象时就把对象的标识放入到 ThreadLocal 中。 - final ThreadLocal<Object> prototypesCurrentlyInCreation

    //AbstractBeanFactory.doGetBean()
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        afterPrototypeCreation(beanName);
    }

    我们看一下 beforePrototypeCreation 方法

    protected void beforePrototypeCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        if (curVal == null) {
            this.prototypesCurrentlyInCreation.set(beanName);
        }
        else if (curVal instanceof String) {
            Set<String> beanNameSet = new HashSet<>(2);
            beanNameSet.add((String) curVal);
            beanNameSet.add(beanName);
            this.prototypesCurrentlyInCreation.set(beanNameSet);
        }
        else {
            Set<String> beanNameSet = (Set<String>) curVal;
            beanNameSet.add(beanName);
        }
    }

    createBean(beanName, mbd, args); 没有什么好解释的,就是创建 Bean,如果使用 @Lazy 修饰的话,则创建代理对象。后续如果 有 AOP 代理的话再次创建代理对象

    接下来 我们来件一下 afterPrototypeCreation(beanName);

    protected void afterPrototypeCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        if (curVal instanceof String) {
            this.prototypesCurrentlyInCreation.remove();
        }
        else if (curVal instanceof Set) {
            Set<String> beanNameSet = (Set<String>) curVal;
            beanNameSet.remove(beanName);
            if (beanNameSet.isEmpty()) {
                this.prototypesCurrentlyInCreation.remove();
            }
        }
    }

    上面的代码也很简单,就是从 ThreadLocal 中移除创建好的对象 (new 、填充属性、aware、beanPostprocessor、init 方法)

  2. 检测循环依赖 - 其实检测在前面,但是先说存放易于理解

    //AbstractBeanFactory.doGetBean()
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }

    来看一下 isPrototypeCurrentlyInCreation 方法

    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        return (curVal != null 
            &&(curVal.equals(beanName) 
            || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }

    我们可以看到当 ThreadLocal 中存在当前的 BeanName 时 返回 true,随后if条件成立抛出异常。

我们来梳理下这两步是如何执行的。假设 A、B 类为原型模式,并且相互依赖并被其他位置引用。

  1. A 开始创建: getBean(A)

  2. 首先检查 A 的循环依赖,发现 ThreadLocal 中并不存在该类,则继续向下执行

  3. A 放入到 ThreadLocal

  4. 开始 A 的创建, 此时发现 A 依赖 B,那么则进入 getBean(B)

  5. 检查 B 的循环依赖,发现 ThreadLocal 中并不存在该类,则继续向下执行

  6. B 放入到 ThreadLocal

  7. 开始 B 的创建, 此时发现 B 依赖 A,那么则进入 getBean(A)

  8. 首先检查 A 的循环依赖,发现 ThreadLocal 中存在该类,抛出异常

属性检测循环依赖-1

所以我们可以看到 Spring 采用了递归方式,来反复执行 ThreadLocal 值的添加和检测,来达到检测循环依赖的目的,非常高明。

检测构造循环依赖

这个检测方法其实与原型循环依赖几乎一样, Spring 使用了一个 Set - 来保存 Bean所依赖的 Bean

  1. A 创建时,

    1. 首先将A标记为创建中

    2. 在构造时发现依赖 B ,则将 B 放到 Set

  2. 随后递归创建 B

    1. 首先将B标记为创建中

    2. 在构造时发现依赖 A ,则将 A 放到 Set

  3. 随后递归创建 A,发现 A 处于构造中,并且在 Set 中存在,随即抛出异常

其他
1
https://gitee.com/superwow/java-notes.git
git@gitee.com:superwow/java-notes.git
superwow
java-notes
java-notes
master

搜索帮助