简体   繁体   中英

Run-time injection: how do I get the most-childish Injector with Guice?

basically my question boils down to how do I get this test to pass:

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

But then perhaps I'm missing the point: stack overflow would have you believe its a sin to do what I'm doing (introducing factories that have a reference to an injector, to which they simply forward you when you ask them to make a product), and that what I'm doing is verging on service-locator, which is a bad move. But I don't see a way around this.

I've got an interface called 'Visualization' that has 7 implementers. When you run, depending on your data set we select a set of these visualizers to create and render. With a factory that has an Injector field, I simply add a method

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

the only alternative I see to having the factory keep an IOC container as a field, is to have 7 of guice's assisted-inject factories, one for each implementation, selected by a switch. That's pretty nasty.

that is one of a couple examples. I'd really just like a nice way to get the most local injector.

edit, to clarify:

There are a number of places where it's convenient to wrap the injector in some kind of decoder. The visualizer was one instance, but there are several more. Thanks for the tip on Map bindings. The problem I have now is that our visualizers (for better or worse... worse I suspect) expect to be instanced every time we need one. So even with the map binding, I'd still need to inject an injector into my visualizer-decoding logic class wouldn't I?

Regarding how many modules I have: I currently have 4 guice modules: a shared one that's got many of my big (truely) singleton services registered with it, then one for our "hosting" UI, then one for our "Problem Setup" UI, and another for our "External tool" UI (In fact there's one more for our testing environment). There's an arbitrary number of the latter two UI modules hanging around, and each of them has a couple objects that are created very late. Meaning without using child modules I believe I'd be left with several map binders, each with bindings being added to them at run-time (and then, presumably for the sake of memory leakage, some kind of removal logic on dispose). It seems to me to be much cleaner to have my (composed!) guice module tree (read: not inheritance tree!)

Thanks for any help, and thanks for the existing suggetsions.

I've got an interface called 'Visualization' that has 7 implementers. When you run, depending on your data set we select a set of these visualizers to create and render.

If your set of visualizers is fixed, consider using plain binding annotations . This is the most simple way intended exactly for distinguishing between multiple implementations of the same interface. You do not need multiple injectors for this.

If your set of visualizers is not fixed (eg these visualizers are plugins), then your task is solved neatly with MapBinder . With it you also do not need multiple injectors, you can define all your Visualization s inside single injector:

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

Then you can inject a Map<Class<Visualization>, Visualization> :

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

You can select arbitrary key, not only Class<Visualizer> , it depends on your business requirements. If visualizers are plugins, you should probably use some kind of plugin identifier as a key.

To answer your first question, you can get the desired injector by adding an explicit bind(DependsOnInjector.class); to ChildModule . Otherwise, " just-in-time bindings created for child injectors will be created in an ancestor injector whenever possible ."

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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