Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice: Binding several objects with different dependencies

Tags:

java

guice

I have the following code using Guice bindings:

public class MyApplication {
    public static void main(String[] args) {
        Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(Foo.class).annotatedWith(Names.named("first")).toInstance(new Foo("firstFoo"));
                bind(Foo.class).annotatedWith(Names.named("second")).toInstance(new Foo("secondFoo"));

                bind(Bar.class).to(BarImpl.class);

                bind(MyApplication.class).asEagerSingleton();
            }
        });
    }

    private @Named("first") Bar first;
    private @Named("second") Bar second;

    static @Value class Foo { String name; }
    static interface Bar {}

    static class BarImpl implements Bar {
        @Inject @Named Foo foo;
    }
}

I'm trying to get a Bar object for both named Foos injected in my application. Basically, it should somehow connect the @Named on Foo with the one on Bar. I have tried several solutions, from putting @Named on everything to writing a custom Provider. The latter didn't work because I don't have access to the value of the @Named annotation inside the provider. I think the solution is somewhere in the line bind(Bar.class).to(BarImpl.class);, telling it to remember the value of the @Named annotation.

My question is, is this possible at all, and if so, how?

like image 896
Jorn Avatar asked Jun 07 '13 15:06

Jorn


1 Answers

It is using PrivateModules. Basically:

A private module's configuration information is hidden from its environment by default. Only bindings that are explicitly exposed will be available to other modules and to the users of the injector. For more explanation see this FAQ entry.

Here is how you'd use it:

protected void configure() {
    install(new PrivateModule() {
        @Override
        protected void configure() {
            // #bind makes bindings internal to this module unlike using AbstractModule
            // this binding only applies to bindings inside this module
            bind(Foo.class).toInstance(new Foo("first"));
            // Bar's foo dependency will use the preceding binding
            bind(Bar.class).annotatedWith(Names.named("first")).to(BarImpl.class);
            // if we'd stop here, this would be useless
            // but the key method here is #expose
            // it makes a binding visible outside as if we did AbstractModule#bind
            // but the binding that is exposed can use "private" bindings
            // in addition to the inherited bindings              
            expose(Bar.class).annotatedWith(Names.named("first"));
        }
    });
    install(new PrivateModule() {
        @Override
        protected void configure() {
            bind(Foo.class).toInstance(new Foo("second"));
            bind(Bar.class).annotatedWith(Names.named("second")).to(BarImpl.class);
            expose(Bar.class).annotatedWith(Names.named("second"));
        }
    });
         bind(MyApplication.class).asEagerSingleton();
    }
}

Now you effectively have 2 Bars each of which look like

static class BarImpl implements Bar {
    @Inject Foo foo;
}

but with the power of PrivateModules have a different implementation bound for the same dependency.

Hope it makes sense.

like image 191
Alen Vrečko Avatar answered Oct 29 '22 13:10

Alen Vrečko