【问题标题】:Run-time injection: how do I get the most-childish Injector with Guice?运行时注入:如何使用 Guice 获得最幼稚的 Injector?
【发布时间】:2013-10-19 00:46:25
【问题描述】:

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

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

但也许我错过了重点:堆栈溢出会让你相信做我正在做的事情是一种罪过(引入引用注入器的工厂,当你问他们时他们会简单地转发给你制造产品),而我正在做的是接近服务定位器,这是一个糟糕的举动。但我看不出有什么办法。

我有一个名为“可视化”的接口,它有 7 个实现者。当您运行时,根据您的数据集,我们选择一组这些可视化工具来创建和渲染。使用具有 Injector 字段的工厂,我只需添加一个方法

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

我认为让工厂保留 IOC 容器作为字段的唯一替代方法是拥有 7 个 guice 的辅助注入工厂,每个实现一个,由开关选择。这太恶心了。

这是几个例子之一。我真的只是想要一种获得最本地注射器的好方法。

编辑,澄清:

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

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

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

【问题讨论】:

    标签: java dependency-injection guice


    【解决方案1】:

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

    【讨论】:

    • RTFM,呵呵,谢谢塔维安。我浏览了该文档一段时间,并且我很确定我之前阅读过createChildInjector 文档,但是直到您现在为我引用这句话之前,我才明白这句话的真正含义。非常感谢。
    • 没问题。如果可能,请考虑 Vladimir 的建议,改为使用绑定注释。
    【解决方案2】:

    我有一个名为“可视化”的接口,它有 7 个实现者。 当您运行时,根据您的数据集,我们选择其中的一组 可视化工具来创建和渲染。

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

    如果您的可视化工具集不固定(例如,这些可视化工具是插件),那么您的任务可以通过 MapBinder 巧妙地解决。有了它,您也不需要多个注入器,您可以在单个注入器中定义所有 Visualizations:

    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&lt;Class&lt;Visualization&gt;, Visualization&gt;:

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

    您可以选择任意键,不仅仅是Class&lt;Visualizer&gt;,这取决于您的业务需求。如果可视化工具是插件,您可能应该使用某种插件标识符作为键。

    【讨论】:

    • 谢谢,我不知道地图活页夹,它会派上用场的。不过,这并不是我所需要的,我确实需要最幼稚的注射器。再次感谢。
    【解决方案3】:

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

    【讨论】:

    • 7 @Provides 具有 7 个自定义绑定注释的方法是很多嘈杂的代码。
    • 您可以将Visualization 实例的列表或映射注入单个@Provides 方法(返回Visualizations)。使用MapBinderSetBinder 会使这变得非常简单。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-05
    • 1970-01-01
    相关资源
    最近更新 更多