[英]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; 蓝图具有两种逻辑成分:
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 一个人为的例子来说明
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.