1

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

1 回答 1

0

一种允许Multibinder在私有模块之间共享 a 的解决方法是将 a 包装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;
        }
    }
}
于 2013-01-11T11:16:22.977 回答