Java框架中循环依赖的解决方案

发布时间:2024年01月04日

微信公众号: 线程
在这里插入图片描述

在一些Java容器技术中,比如IOC容器和DI容器,通常需要处理一种叫作循环依赖的情况。

循环依赖出现的场景

循环依赖指的是两个或更多的bean彼此相互持有,最终形成一个环。

比如在下面这个例子中ComponentWithInjectConstructor是依赖于Dependency的, 同时DependencyDependedOnComponent依赖于component

public class ComponentWithInjectConstructor implements Component {
    private Dependency dependency;
    //表示注入
    @Inject
    public ComponentWithInjectConstructor(Dependency dependency) {
        this.dependency = dependency;
    }
}

public class DependencyDependedOnComponent implements Dependency {
    private Component component;

    @Inject
    public DependencyDependedOnComponent(Component component) {
        this.component = component;
    }
}

DIcontainer的对外接口是这样的:

context.bind(Component.class,  ComponentWithInjectConstructor.class);
context.bind(Dependency.class, DependencyDependedOnComponent.class);

context.get(Component.class);

在实现绑定bind时,我们使用了一个映射(map),以注入的接口作为键。接口的实例被封装在一个类中,这个类实现了Provider接口。

要获取已注入的实例,首先需要创建Provider类的实例,然后调用其get方法。

Map<Class<?>, Provider<?>> providers = new HashMap<>();

//bind注入
providers.put(type, new ConstructorInjectionProvider(type, injectConstructor));

//get提取
//根据注入的接口获得provider接口
Optional.ofNullable(providers.get(type)).map(provider -> provider.get());

//providers接口的get方法
//获取provider的实例, 然后调用get方法, 创建实例, 返回注入的实例
Object[] dependencies = stream(injectConstructor.getParameters())
    .map(p -> get(p.getType()))
    .toArray(Object[]::new);

在处理循环构造时,我们会遇到以下情况:
首先,根据Component查找类,找到了ComponentWithInjectConstructor。然后遍历其构造函数的参数,发现参数类型为Dependency。
接着,根据Dependency查找类,找到了DependencyDependedOnComponent。再次遍历其构造函数的参数,发现参数类型为Component,形成了一个循环依赖的环。最终程序会一种处在一种死循环中。

循环依赖的解决方案

如前所述,循环依赖就是在对象关系图中形成了一个环。我们需要采取措施来打破这个循环。

解决方法相对简单,在构造对象时,我们首先将当前正在构造的对象压入栈中。在下一次判断时,检查对象是否已经在构造中,如果是,则说明存在循环依赖,应该抛出异常。
若构造完成,则将对象从栈中弹出。这一机制能有效防止循环依赖的问题。

对应的代码逻辑如下, 这里我们直接使用一个全局条件变量, 也可以达到上面的效果。

if (constructing) throw new CyclicDependenciesFoundException(componentType);

try {
    //表示当前的provider正在构造
    constrcuting = true;
    Object[] dependencies = stream(injectConstructor.getParameters())
         .map(p -> get(p.getType()))
        .toArray(Object[]::new);

    return injectConstructor.newInstance(dependencies);
} finally {
    constructing = false;
}

因为不断的查找try中出现了死循环,finally中的东西永久不会执行, 并且当在一个依赖中查找到component的时候, 会发现constructing为true, 所以就会抛出异常。

文章来源:https://blog.csdn.net/user_03/article/details/135296627
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。