limpb...@gmail.com
unread,Jun 11, 2009, 10:33:53 PM6/11/09Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to google-guice
I'm impressed! We have an open bug for this (issue 319) but I haven't
given it much attention because you have to have all of the moving
parts in the exact wrong configuration to cause the problem. The
immediate fix is to just use explicit bindings in the child injector.
-- JIT bindings and circular dependencies --
For your entertainment, here's what's happening behind the scenes.
Suppose we have these classes:
class Foo {
@Inject Provider<Bar> bar;
@Inject Provider<Baz> baz;
}
class Bar {
@Inject Provider<Foo> foo;
}
interface Baz {}
1. You request a just-in-time (JIT) binding Foo from an Injector.
Here's a picture of our Injector:
Injector: bindings={Stage, Injector}, jitbindings={}
2. While constructing the binding for Foo, Guice eagerly adds the
partial binding for Foo to its collection of JIT bindings. That way,
Foo's dependencies can circularly depend on Foo. Later on, if the Foo
binding cannot be completed (due to an error), it will be removed from
the partial set.
Injector: bindings={Stage, Injector}, jitbindings={Foo}
3. While building Foo, there's some dependency Bar that circularly
depends on Foo. Bar's binding gets added to the JIT bindings
collection, using the partial Foo binding as a dependency.
Injector: bindings={Stage, Injector}, jitbindings={Foo, Bar}
4. Still binding Foo, we find some other dependency Baz that cannot be
resolved. So the Bar binding fails, and therefore the Foo binding
fails. We remove both from the JIT bindings collection. But it's too
late! The Bar binding is already in the collection, and it already
depends on Foo. So we've accidentally polluted the JIT bindings
collection.
Injector: bindings={Stage, Injector}, jitbindings={Bar}
-- JIT bindings and child injectors --
Without child injectors, this binding pollution problem exists, but it
is not a problem in practice. That's because although the jitBindings
collection can get polluted, whenever it is polluted there will also
be a big nasty ConfigurationException to accompany it. Something to
this effect:
"Guice configuration errors:
Cannot create binding for Baz
while resolving Foo.baz"
And so this problem has existed forever, but nobody has really paid
any attention to it.
-- JIT bindings and child injectors and circular dependencies --
Now that we have child injectors, the problem has become more
treacherous. Whenever you ask a child injector for a binding, it
follows this algorithm:
1. Is there any explicit binding? If so, return it.
2. First, ask the parent injector to create a JIT binding. If that
succeeds, return it. Otherwise ignore any errors.
3. Attempt to create the binding in the child injector.
When we combine these two reasonable behaviours, we silently pollute
the parent's JIT bindings. And so you end up with a half-baked binding
that blows up with an IllegalStateException when it's used.
So I suppose I'd best fix the pollution issue, which will fix your
problem. Sorry for the pain; hopefully you're sympathetic to the
complexities involved here!
Cheers,
Jesse