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());
}
}
'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
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
Well, technically I guess because of the bug that mculls pointed out, it's no problem that we're not warning on Provider
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);
}
Most helpful comment
@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:
Any chance this bug will get addressed?