1

I have the following code:

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;

import com.google.inject.*;
import org.junit.jupiter.api.*;

public class GuiceTest {

  public static void main(String[] args) {
    new GuiceTest().test();
  }

  @Test
  public void test() {
    Injector injector = Guice.createInjector();
    MainObject obj1 = injector.getInstance(MainObject.class);
    MainObject obj2 = injector.getInstance(MainObject.class);

    // Ensure two distinct objects.
    assertThat(obj1, is(not(sameInstance(obj2))));

    Dependency dep1 = obj1.getDependency();
    Dependency dep2 = obj2.getDependency();

    for (int i = 0; i < 10; i++) {
      // Ensure only one dependency instance per distinct object
      assertThat(dep1, is(sameInstance(obj1.getDependency())));
      assertThat(dep2, is(sameInstance(obj2.getDependency())));
    }

    // Ensure each object has its own dependency.
    assertThat(dep1, is(not(sameInstance(dep2)))); // fails
  }

  static class ChildModule extends PrivateModule {

    @Override
    protected void configure() {
    }
  }

  static class MainObject {

    private final Injector injector;

    @Inject
    MainObject(Injector injector) {
      this.injector = injector.createChildInjector(new ChildModule());
    }

    Dependency getDependency() {
      return injector.getInstance(Dependency.class);
    }
  }

  @Singleton
  static class Dependency {
  }

}

The goal is to have one (and only one) Dependency per MainObject. This is why I declared it @Singleton and called it from the child injector.

It looks like despite I use a child injector, the singleton is registered in the parent one.

What am I doing wrong?

Notes:

  • This is only a subset of the full use case. The full use case contains 40+ dependencies.
  • Each dependency is optional, so I thought about keeping them in the injector and accessing them through the injector. Their existence is checked with injector.getExistingBinding(Key.of(Class)).
  • The module may not define the dependencies, the dependencies need to be implemented as "implicit" (so that I can use them easily without having to define them in the module).
Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
  • All you need to do is add `bind(Dependency.class);` to `ChildModule`. – Tavian Barnes Dec 21 '16 at 20:30
  • @TavianBarnes Sorry I forgot to mention that I don't know all the dependencies at the compile time. My full use case is a kind of Guava's `ClassToInstanceMap` that I populate with injected dependencies. I thought an injector would be fit here. Isn't that the case? – Olivier Grégoire Dec 21 '16 at 20:41

1 Answers1

1

According to the recursive Guice binding-resolution docs, implicit bindings are created in parent bindings as far up as possible:

  1. Use explicit bindings.
    • If the binding links to another, follow this resolution algorithm for that.
    • If the binding specifies an instance, return that.
    • If the binding specifies a provider, use that.
  2. Ask a parent injector. If this injector has a parent injector, ask that to resolve the binding. If it succeeds, use that. Otherwise proceed.

[ ... ]

  1. Use a single @Inject or public no-arguments constructor.
    • Validate bindings for all dependencies — the constructor's parameters, plus @Inject methods and fields of the type and all supertypes.
    • Invoke the constructor.
    • Inject all fields. Supertype fields are injected before subtype fields.
    • Inject all methods. Supertype methods are injected before subtype methods.

The docs for createChildInjector reinforce this assertion:

Just-in-time bindings created for child injectors will be created in an ancestor injector whenever possible. This allows for scoped instances to be shared between injectors. Use explicit bindings to prevent bindings from being shared with the parent injector. Optional injections in just-in-time bindings (created in the parent injector) may be silently ignored if the optional dependencies are from the child injector.

This implies that unless you binding instances or singletons explicitly inside the child injector module (i.e. making them explicit), you're not going to have much luck preserving the singleton-inside-a-child-injector behavior you're looking for.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • What if I use a custom scope, defined in my `ChildModule` (which is instanciated for each child injector)? Will that make it? – Olivier Grégoire Dec 21 '16 at 20:15
  • @OlivierGrégoire Sounds like this solution here, noting the extra boilerplate during construction: [Custom Guice Scope, or a better approach?](http://stackoverflow.com/q/9942782/1426891) (Incidentally, the original request is very similar to yours.) – Jeff Bowman Dec 21 '16 at 20:28
  • 1
    Thank you. The link you provided solves totally my use-case! I marked this question as a duplicate of the one you linked. – Olivier Grégoire Dec 21 '16 at 22:00