0

I have seen several usage in a project I am working as the following (I am posting the essence of the code so it is easier to read):

public static void main(Strings[] args) {
    Injector i = Guice.createInjector(new MyModule());
    i.getInstance(Foo.class);
}

class Foo {}

class MyModule extends AbstractModule {
    @Provides
    public Foo getFoo(Injector injector) {
        return new Foo();
    }
}

When I tried to print the injector.toString() in the module I think Guice somehow bound the Injector to the instance i itself.

Like for example

public static void main(Strings[] args) {
    Injector i = Guice.createInjector(new MyModule());
    Injector j = i.getInstance(Injector.class);
    if (i == j) {
        // true
    }
}
....

So in this case can someone please shed some light on how Guice obtains the instance of Injector in the getFoo(injector) method? Thanks!

Peng
  • 381
  • 4
  • 11
  • I think I found in the Guice API doc that each Injector contains a default binding of itself... https://google.github.io/guice/api-docs/3.0/javadoc/com/google/inject/Injector.html So this explained why injector will return itself if trying to get an instance of Injector...Still I would appreciate it very much if anyone can kindly explain the internal mechanism of this. – Peng Dec 21 '18 at 02:52

1 Answers1

1

Injector is injectable in your graph, and Guice will supply arguments to @Provides methods. However, you should only ever need to inject your Injector in very rare cases, and there is no need to request an Injector if you don't use it.


@Provides
public Foo getFoo(Dep1 dep1, Dep2 dep2) {
    return new Foo(dep1, dep2);
}

This assumes Guice can create instances of Dep1 and Dep2 (via constructors, bindings, or @Provides methods), and is roughly equivalent to if you had annotated a constructor in Foo. Guice will inject instances of Dep1 and Dep2 when calling the Foo constructor, and Guice will provide the instances when calling your @Provides Foo getFoo method above.

@Inject public Foo(Dep1 dep1, Dep2 dep2) { /* ... */ }

As in the documentation for the Injector class that you linked (latest docs available here):

Contains several default bindings:

  • This Injector instance itself
  • A Provider<T> for each binding of type T
  • The Logger for the class being injected
  • The Stage in which the Injector was created

Consequently, you may put Dep1, Dep2, Provider, Provider, Injector, or Provider<Injector> into your @Provides method arguments or @Inject constructor arguments (or @Inject fields, etc). Internally, Injector support happens in InjectorShell.java as a special case, because parent and child Injectors will return different instances of Injector as an exception to Guice's rules about parents and children with the same bindings.


Why would you want to inject an Injector? For the sake of reading and testing, it is usually better to inject the dependency you need (e.g. Dep1) or its provider (e.g. Provider<Dep1>). You would only need to inject the Injector if you need to pass it into some other object, like a legacy Service Locator that you've adapted to use your Guice Injector, or if you need to use the Injector reflectively via Guice's SPI or to get an instance based on a Class object. These are rare, and not likely to be necessary in most of your classes or @Provides methods.

/** Exceptions, generics, and casting omitted for readability. */
@Provides SomeInterface getSomeInterface(
    MyAppPreferences preferences,
    Injector injector) {
  Class<?> clazz = Class.forName(preferences.getSomeInterfaceImplName());
  return injector.getInstance(clazz);
}

See also: Accessing Guice injector in its Module?

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251