Injecting named Guice singleton

14,352

Solution 1

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

}

Solution 2

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.

Share:
14,352
IAmYourFaja
Author by

IAmYourFaja

my father is a principal at burgoyne intnl and got me this job programming lisp and development. I aspire to unittesting with a concentration in mobile platforms.

Updated on June 17, 2022

Comments

  • IAmYourFaja
    IAmYourFaja almost 2 years

    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.

  • IAmYourFaja
    IAmYourFaja about 11 years
    Thanks @Bradley T. Hughes (+1) - however your suggestion doesn't change anything. The unit test still passes, regardless of what you name the Fizz. You mentioned something that I thought was interesting. You said "I haven't actually tried a setup like yours...", however, from your answer I deduce that you seem to be fairly comfortable using Guice. So it makes me wonder: Is this a bizarre setup that I'm using? I thought the point of Guice was to use Classes and Annotations together to pinpoint exactly what type/config you want injected...
  • IAmYourFaja
    IAmYourFaja about 11 years
    ...so am I doing something here that is out of the ordinary? If so, why?!? Where am I misunderstanding the normal usage of Guice? Thanks again!
  • IAmYourFaja
    IAmYourFaja about 11 years
    Thanks @condit (+1) - that was an error on my part in typing up this question. I've edited my original question to show that I'm actually calling injector.getInstance, not injector.inject. Does that change anything for you? Is getInstance not correct for me to be using here? If so, why, and what is? Thanks again!
  • condit
    condit about 11 years
    In your test you're asking the injector for an instance of Fizz which it will always be able to provide you (because of your @Provides method). I think you want to get an instance of MyPOJO instead. Note that the Fizz you're getting is unrelated to the Fizz member of the test. So changing the @Named will have no effect in this case.
  • IAmYourFaja
    IAmYourFaja about 11 years
    You nailed it. I just asked it to inject a MyPOJO and it correctly injected it's Fizz property with my special fizz. Mystery solved - just a poorly implemented test method. Thanks again!
  • Bradley T. Hughes
    Bradley T. Hughes about 11 years
    I don't think it's a bizarre setup. And to be honest, I've not used Guice that much, so I'm certainly no expert. The difference is that the code I am working on uses the same annotation to inject two different types (one single instance of each).