Guice: @Inject Injector does not work with child injectors

Created on 4 Jan 2016  路  17Comments  路  Source: google/guice

The testcase below works, if the injector is created by:

Guice.createInjector(new ChildModule());

it fails if the injector is created by:

Guice.createInjector().createChildInjector(new ChildModule());

The error is:

com.google.inject.ConfigurationException: Guice configuration errors:

1) Unable to create binding for guice.GuiceTest$IOne. It was already configured on one or more child injectors or private modules
bound at guice.GuiceTest$ChildModule.configure(GuiceTest.java:39)
If it was in a PrivateModule, did you forget to expose the binding?
while locating guice.GuiceTest$IOne

package guice;

import static org.junit.Assert.assertNotNull;

import org.junit.Test;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;

public class GuiceTest {

  public interface IOne {
  }

  public static class One implements IOne {
  }

  public interface ITwo {
    IOne getOne();
  }

  public static class Two implements ITwo {
    @Inject Provider<Injector> provider;

    @Override
    public IOne getOne() {
      return provider.get().getInstance(IOne.class);
    }
  }

  public static class ChildModule extends AbstractModule {

    @Override
    protected void configure() {
      bind(IOne.class).to(One.class);
      bind(ITwo.class).to(Two.class);
    }

  }

  @Test
  public void thisTestWorks() {
    Injector injector = Guice.createInjector(new ChildModule());
    ITwo two = injector.getInstance(ITwo.class);
    assertNotNull(two.getOne());

  }

  @Test
  public void thisTestFails() {
    Injector injector = Guice.createInjector().createChildInjector(new ChildModule());
    ITwo two = injector.getInstance(ITwo.class);
    assertNotNull(two.getOne());
  }
}

Most helpful comment

This may be a problem with the way injectors are injected into things from child injectors. Looks like it's always giving you the parent injector.

@sameb, that does, indeed, seem to be the bug - instances obtained through child injectors are handed the parent injector in some (but not all) cases:

package com.example.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.name.Named;

import static com.google.inject.name.Names.named;

public class GuiceBug {
    interface IntContainer {
        int value();
    }

    static final class HashCodeContainer implements IntContainer {
        final int value;

        @Inject
        HashCodeContainer(final Injector injector) {
            this.value = injector.hashCode();
        }

        @Override
        public int value() {
            return value;
        }
    }

    static final class Foo {
        final int number;

        @Inject
        Foo(final Injector injector) {
            this.number = injector.hashCode();
        }
    }

    static final class M extends AbstractModule {
        @Override
        protected void configure() {
            //#1 gets the parent injector, bugged:
            bind(IntContainer.class).annotatedWith(named("1")).to(HashCodeContainer.class);

            //#2 gets the parent injector, bugged:
            bind(HashCodeContainer.class).annotatedWith(named("2")).to(HashCodeContainer.class);

            //#3 gets the child injector, which is correct...
            bind(Foo.class);
        }

        //#4 gets the child injector, which is correct...
        @Provides @Named("4") IntContainer provide(final Injector injector) {
            return new HashCodeContainer(injector);
        }
    }

    public static void main(String[] args) {
        final Injector parent = Guice.createInjector();
        final Injector child  = parent.createChildInjector(new M());

        final IntContainer four = child.getInstance(Key.get(IntContainer.class, named("1")));
        check(four.value() == child.hashCode(), "1");
        final HashCodeContainer five = child.getInstance(Key.get(HashCodeContainer.class, named("2")));
        check(five.value() == child.hashCode(), "2");
        final Foo foo = child.getInstance(Foo.class);
        check(foo.number == child.hashCode(), "3");
        final IntContainer six = child.getInstance(Key.get(IntContainer.class, named("4")));
        check(six.value() == child.hashCode(), "4");
    }

    static void check(final boolean pass, final String n) {
        if (pass) {
            System.out.printf("Scenario #%s: PASSED%n", n);
        } else {
            System.err.printf("Scenario #%s: FAILED%n", n);
        }
    }
}

Any chance this bug will get addressed?

All 17 comments

'Two' is being bound in the parent injector because the ChildModule has no separate bind(Two.class) statement. 'Two' is also injecting the Injector (a generally bad idea), so ends up injecting the parent injector. Later, when calling two.getOne, it is trying to retrieve 'IOne' from the parent injector, but it was bound in the child injector, so you get that error.

You have a few ways of fixing the problem:

1) Add binder().requireExplicitBindings() to force bindings to live in the injector that created them.
2) Add explicit bind(One.class) & bind(Two.class) statements to force the bindings to live in the child injector.
3) Stop injecting the injector and instead inject Provider directly, which would cause you to fail faster (on createChildInjector) instead of later during two.getOne().

Thanks for your superfast response! I changed the ChildModule to

  public static class ChildModule extends AbstractModule {

    @Override
    protected void configure() {
      binder().requireExplicitBindings();
      bind(One.class);
      bind(Two.class);
      bind(IOne.class).to(One.class);
      bind(ITwo.class).to(Two.class);
    }

  }

but the error is the same.

BTW I can not inject a Provider in my code, because its too generic. It looks like this

Object createInstance(java.lang.Class<?> type) {
  Object instance = createObjectFromEclipseModelingFrameWork(type);
  injector.injectMembers(instance);
  return instance;
}

Can you paste the full stack trace of the error?

com.google.inject.ConfigurationException: Guice configuration errors:

1) Unable to create binding for guice.GuiceTest$IOne. It was already configured on one or more child injectors or private modules
    bound at guice.GuiceTest$ChildModule.configure(GuiceTest.java:41)
  If it was in a PrivateModule, did you forget to expose the binding?
  while locating guice.GuiceTest$IOne

1 error
    at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
    at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
    at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
    at guice.GuiceTest$Two.getOne(GuiceTest.java:30)
    at guice.GuiceTest.thisTestFails(GuiceTest.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

This may be a problem with the way injectors are injected into things from child injectors. Looks like it's always giving you the parent injector.

If you inject the Injector directly rather than indirectly via a Provider then it works.

In other words change:

  public static class Two implements ITwo {
    @Inject Provider<Injector> provider;

    @Override
    public IOne getOne() {
      return provider.get().getInstance(IOne.class);
    }
  }

to:

  public static class Two implements ITwo {
    @Inject Injector injector;

    @Override
    public IOne getOne() {
      return injector.getInstance(IOne.class);
    }
  }

There's no advantage to using Provider<Injector> here, since the Injector already exists at the time of injection - generally you use Provider when you need to defer creation or break a dependency cycle.

When I use the Injector w/o Provider, then - in our production code - I get the warning:

AssistedInject factory ...will be slow because class ... has assisted Provider dependencies or injects the Injector.

Changing the code from Injector to Provider<Injector> did really speed things up here.

Ohhh, if you don't get the warning from a Provider, then that's likely a bad bug. I should be scanning for that and warning too.

Well, technically I guess because of the bug that mculls pointed out, it's no problem that we're not warning on Provider. However if Provider is fixed to inject the child injector, then we'd need to add the warning.

This may be a problem with the way injectors are injected into things from child injectors. Looks like it's always giving you the parent injector.

@sameb, that does, indeed, seem to be the bug - instances obtained through child injectors are handed the parent injector in some (but not all) cases:

package com.example.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.name.Named;

import static com.google.inject.name.Names.named;

public class GuiceBug {
    interface IntContainer {
        int value();
    }

    static final class HashCodeContainer implements IntContainer {
        final int value;

        @Inject
        HashCodeContainer(final Injector injector) {
            this.value = injector.hashCode();
        }

        @Override
        public int value() {
            return value;
        }
    }

    static final class Foo {
        final int number;

        @Inject
        Foo(final Injector injector) {
            this.number = injector.hashCode();
        }
    }

    static final class M extends AbstractModule {
        @Override
        protected void configure() {
            //#1 gets the parent injector, bugged:
            bind(IntContainer.class).annotatedWith(named("1")).to(HashCodeContainer.class);

            //#2 gets the parent injector, bugged:
            bind(HashCodeContainer.class).annotatedWith(named("2")).to(HashCodeContainer.class);

            //#3 gets the child injector, which is correct...
            bind(Foo.class);
        }

        //#4 gets the child injector, which is correct...
        @Provides @Named("4") IntContainer provide(final Injector injector) {
            return new HashCodeContainer(injector);
        }
    }

    public static void main(String[] args) {
        final Injector parent = Guice.createInjector();
        final Injector child  = parent.createChildInjector(new M());

        final IntContainer four = child.getInstance(Key.get(IntContainer.class, named("1")));
        check(four.value() == child.hashCode(), "1");
        final HashCodeContainer five = child.getInstance(Key.get(HashCodeContainer.class, named("2")));
        check(five.value() == child.hashCode(), "2");
        final Foo foo = child.getInstance(Foo.class);
        check(foo.number == child.hashCode(), "3");
        final IntContainer six = child.getInstance(Key.get(IntContainer.class, named("4")));
        check(six.value() == child.hashCode(), "4");
    }

    static void check(final boolean pass, final String n) {
        if (pass) {
            System.out.printf("Scenario #%s: PASSED%n", n);
        } else {
            System.err.printf("Scenario #%s: FAILED%n", n);
        }
    }
}

Any chance this bug will get addressed?

So, the weird workaround that someone at my company seems to have figured out is that if you inject an injector along with something exclusively from the parent injector scope, the right injector gets injected.

Not sure whether this issue is related to InjectiongTheInjector
In User Guide, it said don't inject the injector, because injecting the injector makes it impossible for Guice to know ahead-of-time that your Dependency Graph is complete,

In previous test case of @avoss, Class Two use a provider of injector, which almost equals to inject the injector

This may be a problem with the way injectors are injected into things from child injectors. Looks like it's always giving you the parent injector.

@sameb, that does, indeed, seem to be the bug - instances obtained through child injectors are handed the parent injector in _some_ (but not all) cases:

package com.example.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.name.Named;

import static com.google.inject.name.Names.named;

public class GuiceBug {
    interface IntContainer {
        int value();
    }

    static final class HashCodeContainer implements IntContainer {
        final int value;

        @Inject
        HashCodeContainer(final Injector injector) {
            this.value = injector.hashCode();
        }

        @Override
        public int value() {
            return value;
        }
    }

    static final class Foo {
        final int number;

        @Inject
        Foo(final Injector injector) {
            this.number = injector.hashCode();
        }
    }

    static final class M extends AbstractModule {
        @Override
        protected void configure() {
            //#1 gets the parent injector, bugged:
            bind(IntContainer.class).annotatedWith(named("1")).to(HashCodeContainer.class);

            //#2 gets the parent injector, bugged:
            bind(HashCodeContainer.class).annotatedWith(named("2")).to(HashCodeContainer.class);

            //#3 gets the child injector, which is correct...
            bind(Foo.class);
        }

        //#4 gets the child injector, which is correct...
        @Provides @Named("4") IntContainer provide(final Injector injector) {
            return new HashCodeContainer(injector);
        }
    }

    public static void main(String[] args) {
        final Injector parent = Guice.createInjector();
        final Injector child  = parent.createChildInjector(new M());

        final IntContainer four = child.getInstance(Key.get(IntContainer.class, named("1")));
        check(four.value() == child.hashCode(), "1");
        final HashCodeContainer five = child.getInstance(Key.get(HashCodeContainer.class, named("2")));
        check(five.value() == child.hashCode(), "2");
        final Foo foo = child.getInstance(Foo.class);
        check(foo.number == child.hashCode(), "3");
        final IntContainer six = child.getInstance(Key.get(IntContainer.class, named("4")));
        check(six.value() == child.hashCode(), "4");
    }

    static void check(final boolean pass, final String n) {
        if (pass) {
            System.out.printf("Scenario #%s: PASSED%n", n);
        } else {
            System.err.printf("Scenario #%s: FAILED%n", n);
        }
    }
}

Any chance this bug will get addressed?

   ` final Injector parent = Guice.createInjector(new M());`

Using the parent injector without the child injector worked for me. Try it out!

In the test cases scenario, guice recursively create the just-in-time binding by use parent.createJustInTimeBindingRecursive at first, this may be the reason why the injected injector is the parent injector.

Just check whether child injector has correct linked key binding before use parent injector, not sure whether this is the correct way to solve this bug.

Also don't know why the travis CI not passed because http 404 not found.

I just spent several hours tracking this down.

edit: I've actually already run into this problem, so its kind've asked & answerd:
https://stackoverflow.com/questions/19461065/run-time-injection-how-do-i-get-the-most-childish-injector-with-guice

Heres my dependency tree & use case:

CommandLineReader [Concrete]: 
  [no deps]

ApplicationRoot [Concrete]:
  Dep1 [Concrete]:
    SharedServiceInterface [Interface via Impl1]
    Common [Concrete]
  Dep2:
    SharedServiceInterface [Interface via Impl2]
    Common [Concrete]

bound like:

class ApplicationRoot{
  ApplicationRoot(Dep1 dep1, Dep2 dep2) { ... }
}
class Dep1{
  private final SharedServiceInterface svc;

  Dep1(Injector injector){
    var child = injector.createChildInjector(binder -> binder.bind(SharedServiceInterface.class).to(Impl1.class))

    svc = child.getInstance(SharedServiceInterface.class)
  }
}
class Dep2{
  private final SharedServiceInterface svc;

  Dep2(Injector injector){
    var child = injector.createChildInjector(binder -> binder.bind(SharedServiceInterface.class).to(Impl2.class))

    svc = child.getInstance(SharedServiceInterface.class)
  }
}

public static void main(string[] args){
  var rootsParent = Guice.createInjector(...);

  rootsParent.getInstance(CommandLineReader.class).handle(args)

  var actualRoot = rootsParent.createChildInjector(binder -> binder.bind(Common.class).asAppropriate())

  actualRoot.getInstance(ApplicationRoot.class) //ERROR: "Common" not bound.
  // stack trace points here,
  // but adding some catch blocks has the stack-trace point to the createChildInjector lines
  // the cause: the 'parentInjector' instance passed into Dep1's ctor is the same as "rootsParent" here.
}

If theres a better way to do this with "just use providers" please let me know. This is on guice 4.2.2

heres an SSCCE:

static class MyNestingType {

    Injector child = null;

    @Inject public MyNestingType(Injector injector){
        child = injector.createChildInjector();
    }
}

@Test
public void when_calling_createChildInjector_with_deps_that_resolve_child_injectors_should_get_child_not_sibling() {

    var granpaInjector = Guice.createInjector();
    var parentInjector = granpaInjector.createChildInjector();
    var instance = parentInjector.getInstance(MyNestingType.class);
    var childInjector = instance.child;

    assertThat(childInjector.getParent()).isEqualTo(parentInjector);
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

felixblaschke picture felixblaschke  路  51Comments

gissuebot picture gissuebot  路  3Comments

gissuebot picture gissuebot  路  117Comments

scottcarey picture scottcarey  路  17Comments

afghl picture afghl  路  5Comments