简体   繁体   English

如何处理多个guice私有模块之间的循环依赖关系?

[英]How to handle circular dependencies across multiple guice private modules?

I am currently writing some framework code that provides a blueprint for services within our platform so that service implementors can focus on the service specific logic rather than the boilerplate integration code. 我目前正在编写一些框架代码,这些代码为我们平台内的服务提供了一个蓝图,以便服务实现者可以专注于服务特定的逻辑,而不是样板集成代码。 Dependency injection is done via guice. 依赖注入是通过guice完成的。

The blueprint has 2 types of logical component; 蓝图具有两种逻辑成分:

  1. 1 and only 1 integration component that integrates the service with the outside world (messaging middleware etc) 1个,也是唯一一个将服务与外界集成的集成组件(消息中间件等)
  2. 1-n business logic components 1-n业务逻辑组件

Each logic component depends on the integration component. 每个逻辑组件均取决于集成组件。

The integration component depends on all the logic components. 集成组件取决于所有逻辑组件。

Since this is framework code, the framework is not aware of any of the concrete details so it is not possible to statically declare the dependencies and form the dependency graph. 由于这是框架代码,因此框架不知道任何具体细节,因此无法静态声明依赖项并形成依赖关系图。 I would like to avoid making service implementors do this because it means they are repeating themselves (just declaring that they have n business logic modules means they have this circular dependency). 我想避免让服务实现者这样做,因为这意味着它们正在重复自己(仅声明它们具有n个业务逻辑模块就意味着它们具有这种循环依赖性)。

My question is what approaches can I take to make this work without making service implementors write that boilerplate code? 我的问题是,在不让服务实现者编写样板代码的情况下 ,我可以采用哪些方法来实现此目的?

Note that multibindings are not available here as, for various reasons outside the scope of this question, each business logic component must be a PrivateModule. 请注意,这里没有多重绑定,因为出于此问题范围之外的各种原因,每个业务逻辑组件都必须是PrivateModule。

A contrived example to illustrate where 一个人为的例子来说明

  1. business logic = ModuleA, ModuleB, ModuleC 业务逻辑=模块A,模块B,模块C
  2. dependency provided by the integration = Wrapper 集成提供的依赖关系=包装器
  3. Integration's dependency on the business logic is modelled by each logic module adding something to the Wrapper 集成对业务逻辑的依赖关系由每个逻辑模块建模,为包装程序添加了一些内容

This example can be made to work by changing 通过更改此示例可以使其工作

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

to

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

ie by actually creating a circular dependency. 即通过实际创建循环依赖项。

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;
        }
    }
}

One workaround, that allows a Multibinder to be shared across private modules, is to wrap the PrivateModule in an AbstractModule implementation that simply installs the PrivateModule and binds the exposed key to the Multibinder 一种允许在私有模块之间共享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;
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM