运行时注射:如何使用Guice获得最幼稚的注射器?

时间:2013-10-19 00:46:25

标签: java dependency-injection guice

基本上我的问题归结为如何让这个测试通过:

private static class DefaultModule extends AbstractModule {
    @Override protected void configure() {
    }
}
private static class ParentModule extends DefaultModule{}
private static class ChildModule extends DefaultModule {}

private static class DependsOnInjector{
    @Inject
    public Injector depdendency;
}

@Test
public void when_instancing_a_class_that_depends_on_an_injector_it_should_yield_the_most_childish(){ 
    //childish :p
    Injector parent = Guice.createInjector(new ParentModule());
    Injector child = parent.createChildInjector(new ChildModule());

    DependsOnInjector objectWithInjector = child.getInstance(DependsOnInjector.class);

    assertThat(objectWithInjector.depdendency).isSameAs(child);
}

但是也许我错过了这一点:堆栈溢出会让你相信做我正在做的事情是一种罪过(引入了一个参考注射器的工厂,当你问他们时他们只是转发给你制作一个产品,而我正在做的就是服务定位器,这是一个不好的举动。但我没有看到解决方法。

我有一个名为'Visualization'的界面,有7个实现者。运行时,根据您的数据集,我们选择一组这些可视化器来创建和渲染。对于具有Injector字段的工厂,我只需添加一个方法

public <TVis extends Visualization> TVis makeVisualization(Class<TVis> desiredVisualizationType){
    return injector.getInstance(desiredVisualizationType);
}

我认为让工厂保留一个IOC容器作为一个字段的唯一选择是拥有7个guice的辅助注入工厂,每个实现一个,由交换机选择。那太讨厌了。

这是几个例子中的一个。我真的很喜欢获得最本地注射器的好方法。

编辑,澄清:

有很多地方可以方便地将注射器包装在某种解码器中。可视化器是一个实例,但还有几个。感谢Map绑定提示。我现在遇到的问题是我们的可视化工具(无论好坏,我怀疑更糟)希望每次需要时都可以实例化。所以即使使用地图绑定,我仍然需要将一个注入器注入我的可视化器 - 解码逻辑类中,不是吗?

关于我有多少个模块: 我目前有4个guice模块:一个共享的模块,它有很多我的大(真正)单身服务注册了它,然后一个用于我们的“托管”用户界面,然后一个用于我们的“问题设置”用户界面,另一个用于我们的“外部”工具“UI(实际上我们的测试环境还有一个)。后两个UI模块中有任意数量,并且每个模块都有一些很晚才创建的对象。没有使用子模块的含义我相信我会留下几个地图绑定器,每个都在运行时添加绑定(然后,大概是为了内存泄漏,处理时的某种删除逻辑)。在我看来,拥有我的(组合!)guice模块树(阅读:不是继承树!)

感谢您的帮助,感谢现有的建议。

3 个答案:

答案 0 :(得分:4)

  

我有一个名为'Visualization'的界面,有7个实现者。   当您运行时,根据您的数据集,我们选择一组这些   用于创建和渲染的可视化工具。

如果您的可视化工具集已修复,请考虑使用普通binding annotations。这是用于区分同一接口的多个实现的最简单方法。你不需要多个注射器。

如果您的可视化工具集未修复(例如,这些可视化工具是插件),那么您的任务将使用MapBinder整齐地解决。有了它,您也不需要多个注射器,您可以在单个注射器内定义所有Visualization

public class VisualizationsModule extends AbstractModule {
    @Override
    protected void configure() {
        MapBinder<Class<Visualization>, Visualization> binder =
            MapBinder.newMapBinder(binder(), new TypeLiteral<Class<Visualization>>() {},
                                   TypeLiteral.get(Visualization.class));
        binder.addBinding(Visualization1.class).to(Visualization1.class);
        binder.addBinding(Visualization2.class).to(Visualization2.class);
        // etc
    }
}

然后你可以注入一个Map<Class<Visualization>, Visualization>

@Inject
public SomeClass(Map<Class<Visualization>, Visualization> visualizers) {
    ...
}

您可以选择任意密钥,而不仅仅是Class<Visualizer>,这取决于您的业务需求。如果可视化器是插件,则应该使用某种插件标识符作为密钥。

答案 1 :(得分:3)

要回答您的第一个问题,您可以通过向bind(DependsOnInjector.class);添加明确的ChildModule来获取所需的注射器。否则,“just-in-time bindings created for child injectors will be created in an ancestor injector whenever possible。”

答案 2 :(得分:2)

如果您只想在运行时获取正确的实例,为什么不能只在模块中添加@Provides方法来实例化Visualization