【问题标题】:How to handle circular dependencies across multiple guice private modules?如何处理跨多个 guice 私有模块的循环依赖?
【发布时间】:2013-01-10 20:48:02
【问题描述】:

我目前正在编写一些框架代码,为我们平台内的服务提供蓝图,以便服务实现者可以专注于服务特定逻辑,而不是样板集成代码。依赖注入是通过 guice 完成的。

蓝图有2种逻辑组件;

  1. 1 个也是唯一一个将服务与外界集成的集成组件(消息中间件等)
  2. 1-n 个业务逻辑组件

每个逻辑组件都依赖于集成组件。

集成组件依赖于所有逻辑组件。

由于这是框架代码,框架不知道任何具体细节,因此不可能静态声明依赖关系并形成依赖关系图。我想避免让服务实现者这样做,因为这意味着他们在重复自己(只是声明他们有 n 个业务逻辑模块意味着他们有这种循环依赖)。

我的问题是我可以采取哪些方法来在不让服务实现者编写该样板代码的情况下完成这项工作?

请注意,这里不提供多重绑定,因为出于此问题范围之外的各种原因,每个业务逻辑组件都必须是 PrivateModule。

一个人为的例子来说明哪里

  1. 业务逻辑 = ModuleA、ModuleB、ModuleC
  2. 集成提供的依赖 = Wrapper
  3. 集成对业务逻辑的依赖由每个逻辑模块建模,向 Wrapper 添加一些东西

这个例子可以通过改变来工作

@Provides @Exposed @Named("result")
public String go(Container in) {
    return in.format();
}

@Provides @Exposed @Named("result")
public String go(@Named("a") Container in, @Named("b") Container in2, @Named("c") Container in3) {
    return in.format();
}

即通过实际创建循环依赖。

import com.google.inject.Exposed;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.google.inject.name.Names;

import java.util.ArrayList;
import java.util.List;

public class GuiceCircularDependencyTest {

    public static void main(String[] args) {
        Injector in = Guice.createInjector(new Owner());
        String result = in.getInstance(Key.get(String.class, Names.named("result")));
        System.out.println("Result is: " + result);
    }

    public static class Owner extends PrivateModule {
        @Override
        protected void configure() {
            bind(Container.class).in(Singleton.class);
            install(new Integration());
            install(new ModuleA());
            install(new ModuleB());
            install(new ModuleC());
            expose(String.class).annotatedWith(Names.named("result"));
        }
    }

    public static class ModuleA extends PrivateModule {

        @Override
        protected void configure() {

        }

        @Provides @Exposed @Named("a")
        public Container go(Container in, Wrapper prefix) {
            in.add(prefix + "A");
            return in;
        }
    }

    public static class ModuleB extends PrivateModule {

        @Override
        protected void configure() {

        }

        @Provides @Exposed @Named("b")
        public Container go(Container in, Wrapper prefix) {
            in.add(prefix + "B");
            return in;
        }
    }

    public static class ModuleC extends PrivateModule {

        @Override
        protected void configure() {

        }

        @Provides @Exposed @Named("c")
        public Container go(Container in, Wrapper prefix) {
            in.add(prefix + "C");
            return in;
        }
    }

    public static class Integration extends PrivateModule {
        @Override
        protected void configure() {
            bind(Wrapper.class).toInstance(new Wrapper("Module"));
            expose(Wrapper.class);
        }

        @Provides @Exposed @Named("result")
        public String go(Container in) {
            return in.format();
        }
    }

    public static class Container {
        private List<String> strings = new ArrayList<>();

        public void add(String string) {
            strings.add(string);
        }

        public String format() {
            return strings.toString();
        }
    }

    public static class Wrapper {
        private final String prefix;

        public Wrapper(String prefix) {
            this.prefix = prefix;
        }

        @Override
        public String toString() {
            return prefix;
        }
    }
}

【问题讨论】:

  • 替换 'go()' 方法对我有用。你看到什么问题? Guice 是否抛出异常?
  • 你指的是哪个 go() 方法?
  • 你说“这个例子可以通过改变来工作..”我用第二个替换了第一个 go 方法: go(Container in) -> go(@Named("a") Container in, @Named("b") 容器 in2, @Named("c") 容器 in3)
  • 啊好的,是的,这是一个可行的解决方案,但是它不符合“不要重复自己”的要求,这就是我所说的“我的问题是我可以采取什么方法来做到这一点在不让服务实现者编写样板代码的情况下工作?”。如果我使用这种方法,那么我会强制框架用户编写(不是很多)这样的代码来制作依赖图形式。我想避免这种解决方案。
  • 所有业务逻辑组件是否都将实现相同的类型?

标签: java guice circular-dependency


【解决方案1】:

一种允许在私有模块之间共享Multibinder 的解决方法是将PrivateModule 包装在AbstractModule 实现中,该实现只需安装PrivateModule 并将暴露的密钥绑定到Multibinder

import com.google.inject.AbstractModule;
import com.google.inject.Exposed;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.name.Named;
import com.google.inject.name.Names;

import java.util.Set;

public class GuiceCircularDependencyTest {

    public static void main(String[] args) {
        Injector in = Guice.createInjector(new Owner());
        String result = in.getInstance(Key.get(String.class, Names.named("result")));
        System.out.println("Result is: " + result);
    }

    public static class Owner extends PrivateModule {
        @Override
        protected void configure() {
            Multibinder<String> multi = Multibinder.newSetBinder(binder(), String.class);
            install(new Integration());
            install(new ModuleWrapper<>(new ModuleA(), multi));
            install(new ModuleWrapper<>(new ModuleB(), multi));
            install(new ModuleWrapper<>(new ModuleC(), multi));
            expose(String.class).annotatedWith(Names.named("result"));
        }
    }

    public static class ModuleWrapper<T> extends AbstractModule {
        private final WrappablePrivateModule<T> inner;
        private final Multibinder<T> multi;

        public ModuleWrapper(WrappablePrivateModule<T> inner,
                             Multibinder<T> multi) {
            this.inner = inner;
            this.multi = multi;
        }

        @Override
        protected void configure() {
            install(inner);
            multi.addBinding().to(inner.getExposedKey());
        }
    }

    public static abstract class WrappablePrivateModule<T> extends PrivateModule {

        @Override
        protected void configure() {

        }

        public abstract Key<T> getExposedKey();
    }

    public static class ModuleA extends WrappablePrivateModule<String> {

        private static final String SUFFIX = "A";

        @Override
        public Key<String> getExposedKey() {
            return Key.get(String.class, Names.named(SUFFIX));
        }

        @Provides @Exposed @Named(SUFFIX)
        public String expose(Wrapper prefix) {
            return prefix + SUFFIX;
        }
    }

    public static class ModuleB extends WrappablePrivateModule<String> {

        private static final String SUFFIX = "B";

        @Override
        public Key<String> getExposedKey() {
            return Key.get(String.class, Names.named(SUFFIX));
        }

        @Provides @Exposed @Named(SUFFIX)
        public String expose(Wrapper prefix) {
            return prefix + SUFFIX;
        }
    }

    public static class ModuleC extends WrappablePrivateModule<String> {

        private static final String SUFFIX = "C";

        @Override
        public Key<String> getExposedKey() {
            return Key.get(String.class, Names.named(SUFFIX));
        }

        @Provides @Exposed @Named(SUFFIX)
        public String expose(Wrapper prefix) {
            return prefix + SUFFIX;
        }
    }

    public static class Integration extends PrivateModule {
        @Override
        protected void configure() {
            bind(Wrapper.class).toInstance(new Wrapper("Module"));
            expose(Wrapper.class);
        }

        @Provides @Exposed @Named("result")
        public String go(Set<String> in) {
            return in.toString();
        }
    }

    public static class Wrapper {
        private final String prefix;

        public Wrapper(String prefix) {
            this.prefix = prefix;
        }

        @Override
        public String toString() {
            return prefix;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-15
    • 2014-08-28
    • 2013-12-21
    • 2016-05-02
    • 1970-01-01
    • 2011-07-26
    • 2015-01-04
    相关资源
    最近更新 更多