IAmYourFaja
IAmYourFaja

Reputation: 56874

Injecting named Guice singleton

I have a simple POJO:

public class MyPOJO {
    @Inject
    private Fizz fizz;

    private Buzz buzz;

    // rest of class omitted for brevity
}

I would like to configure my Guice module such that there are two types of Fizzes that it injects:

  1. A special, globally-singleton Fizz instance; and
  2. Other (non-special) Fizz instances

I want MyPOJO to be injected with the special/singleton instance. So I modified my code:

public class MyPOJO {
    @Inject @Named("Special-Fizz")
    private Fizz fizz;

    private Buzz buzz;

    // rest of class omitted for brevity
}

Then in my module:

public class MyModule extends AbstractModule {
    @Override
    public void configure() {
        bind(Fizz.class).annotatedWith(
            Names.named("Special-Fizz"))
            .to(Fizz.class);

        // Other bindings here...
    }

    @Provides @Singleton
    private Fizz providesFizz() {
        return new Fizz(true, "Special", 12.0);
    }
}

But when I try to unit test (JUnit 4.10) this:

public class MyTest {
    @Named("Special-Fizz") private Fizz specialFizz;

    @Test
    public void usingNamedShouldInjectSpecialFizz() {
        MyModule mod = new MyModule();
        Injector injector = Guice.createInjector(mod);

        specialFizz = injector.getInstance(Fizz.class);

        Assert.assertTrue(specialFizz != null);
    }
}

This passes. So far, so good. But then if I change the name of the specialFizz field:

    @Named("Special-Fuzz-This-Shouldnt-Work") private Fizz specialFizz;

And re-run the test, it still passes. Why?!? Where am I going astray here? Thanks in advance.

Upvotes: 5

Views: 15877

Answers (2)

condit
condit

Reputation: 10962

Very odd. Guice should complain if it can't find a Named binding that it's injecting. I'm a bit confused by your test, though. I don't know what injector.inject does. Do you mean injectMembers? It might make more sense to actually get an instance of your POJO and make sure that it's working the way you expect. Maybe something like:

public class FizzTest {

  public static class MyModule extends AbstractModule {
    @Override
    protected void configure() {
    }

    @Provides
    @Singleton
    @Named("Special-Fizz")
    public Fizz providesFizz() {
      return new Fizz(true);
    }
  }

  public static class Fizz {
    boolean special = false;
    public Fizz() {}
    public Fizz(boolean special) {
      this.special = special;
    }
  }

  public static class MyPOJO {
    @Inject @Named("Special-Fizz")
    private Fizz fizz;

    @Inject
    private Fizz otherFizz;
  }

  @Test
  public void test() {
    MyModule mod = new MyModule();
    Injector injector = Guice.createInjector(mod);

    MyPOJO pojo = injector.getInstance(MyPOJO.class);
    assertTrue(pojo.fizz.special);
    assertTrue(!pojo.otherFizz.special);
  }

}

Upvotes: 5

Bradley T. Hughes
Bradley T. Hughes

Reputation: 592

Guice sees the @Provides method and happily uses that to inject the Fizz. If you want to have a special instance of Fizz, you can drop the annotations from the providesFizz() method and instead bind with

bind(Fizz.class)
    .annotatedWith(Names.named("Special-Fizz")
    .toInstance(providesFizz());

This way, you tell Guice exactly which Fizz to use as the "Special-Fizz", while still letting it inject Fizz "normally" otherwise.

Disclaimer: I haven't actually tried a setup like yours, but I have used one similar. Let me know if it works or not.

Upvotes: 2

Related Questions