简体   繁体   中英

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 Fizz es 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.

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

}

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.

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