Two interfaces, one implementation - How many instances?

61 views
Skip to first unread message

Esko Luontola

unread,
Sep 14, 2008, 10:22:16 AM9/14/08
to google-guice
I'm writing an application server (http://dimdwarf.sourceforge.net/)
and just begun using Guice with it. It works as I intended, but there
is one thing which does not fit into my mental model about how the
bindings work:

I have bindings:

bind(EntityLoader.class).to(EntityManagerImpl.class);
bind(ReferenceFactory.class).to(EntityManagerImpl.class);

and a class:

@TaskScoped
public class EntityManagerImpl implements ReferenceFactory,
EntityLoader, TransactionListener { ... }

(The task scope is a custom scope, similar to request scope. A task is
a transactional unit of work.)



In the following code, factory and loader point to the same instance
(factory == loader):

ReferenceFactory factory =
injector.getInstance(ReferenceFactory.class);
EntityLoader loader =
injector.getInstance(EntityLoader.class);

This is as it should be (there must be exactly one EntityManagerImpl
instance per task, because it keeps a list of all entities which have
been loaded in memory), but from the binding configuration it is not
intuitive to me, that _why_ it works this way.



The above bindings appear to be functionally equivalent with the
following bindings (when the @TaskScoped annotation is removed from
the EntityManagerImpl class):

bind(EntityManagerImpl.class).in(TaskScoped.class);
bind(EntityLoader.class).to(EntityManagerImpl.class);
bind(ReferenceFactory.class).to(EntityManagerImpl.class);

But my initial intuition was that it would be equivalent with the
following, which creates two instances of EntityManagerImpl per task:


bind(EntityLoader.class).to(EntityManagerImpl.class).in(TaskScoped.class);

bind(ReferenceFactory.class).to(EntityManagerImpl.class).in(TaskScoped.class);



Is there some documentation which would help me to reach a correct
mental model on how the scopes and bindings are resolved? Is there
some way to print a graph of all the bindings, scopes and instances,
so that I could see how Guice exactly resolves the bindings of a given
module?

Robbie Vanbrabant

unread,
Sep 14, 2008, 11:30:56 AM9/14/08
to google...@googlegroups.com
On Sun, Sep 14, 2008 at 4:22 PM, Esko Luontola <esko.l...@gmail.com> wrote:

The above bindings appear to be functionally equivalent with the
following bindings (when the @TaskScoped annotation is removed from
the EntityManagerImpl class):

       bind(EntityManagerImpl.class).in(TaskScoped.class);
       bind(EntityLoader.class).to(EntityManagerImpl.class);
       bind(ReferenceFactory.class).to(EntityManagerImpl.class);

The best way to explain that is that to(...) always refers to another binding, implicit or explicit (I think you can also specify a Key there). So in this case, the second and third binding refer to the first binding, which is TaskScoped. This implicitly scopes these to be TaskScoped as well.

 
But my initial intuition was that it would be equivalent with the
following, which creates two instances of EntityManagerImpl per task:

bind(EntityLoader.class).to(EntityManagerImpl.class).in(TaskScoped.class);

bind(ReferenceFactory.class).to(EntityManagerImpl.class).in(TaskScoped.class);

In this case there is no binding for EntityManagerImpl, so Guice uses the implicit, no-scope binding to the type itself. Both bindings thus receive a different instance that they will @TaskScope.


Is there some documentation which would help me to reach a correct
mental model on how the scopes and bindings are resolved?

See Jesse's initial work at http://code.google.com/p/google-guice/issues/detail?id=213
Also note that my book has excellent coverage on what I explained above and more: http://www.apress.com/book/view/1590599977

Cheers
Robbie

Esko Luontola

unread,
Sep 14, 2008, 2:55:08 PM9/14/08
to google-guice
On Sep 14, 6:30 pm, "Robbie Vanbrabant" <robbie.vanbrab...@gmail.com>
wrote:
> The best way to explain that is that to(...) always refers to another
> binding, implicit or explicit (I think you can also specify a Key there).

Thanks. That explanation helped me to understand it.

Esko Luontola

unread,
Sep 14, 2008, 8:35:14 PM9/14/08
to google-guice
I looked at the source, and actually all the to(...) methods delegate
to the to(Key) method. Also the javadocs of to() say that it "binds to
another binding".

Josh McDonald

unread,
Sep 14, 2008, 8:53:24 PM9/14/08
to google...@googlegroups.com
Correct me if i'm wrong, but when you do this:

bind(EntityManagerImpl.class).in(TaskScoped.class);
bind(EntityLoader.class).to(EntityManagerImpl.class);
bind(ReferenceFactory.class).to(EntityManagerImpl.class);

You're asking guice to do 3 things:

  1. Bind EntityManagerImpl in TaskScoped
  2. Bind EntityLoader tp EntityManagerImpl
  3. Bind ReferenceFactory to EntityManagerImpl
When you do this:

bind(EntityLoader.class).to(EntityManagerImpl.class).in(TaskScoped.class);
bind(ReferenceFactory.class).to(EntityManagerImpl.class).in(TaskScoped.class);

You're asking for two things:
  1. Bind EntityLoader in TaskScoped, implemented by EntityManagerImpl
  2. Bind ReferenceFactory in TaskScoped, coincidently also implemented by EntityManagerImpl
In the second part, there's nothing telling Guice that EntityManagerImpl is in TaskScope, only that EntityLoader and ReferenceFactory are. So the system knows that for each Task you've got one EntityLoader and one ReferenceFactory, it doesn't have a clue (or care) how mamy EntityManagerImpls are required to achieve that. Everything following bind(foo) applies to the binding for Foo, it doesn't "leak out" into EntityManagerImpl If you want to bind EntityManagerImpl to another impl or to a scope, you have to do it either with the annotations or a binding of its own.

-Josh
--
"Therefore, send not to know For whom the bell tolls. It tolls for thee."

http://flex.joshmcdonald.info/

:: Josh 'G-Funk' McDonald
:: 0437 221 380 :: jo...@gfunk007.com

Dhanji R. Prasanna

unread,
Sep 14, 2008, 10:34:44 PM9/14/08
to google...@googlegroups.com
Yes, you will have 2 task-scoped EM bindings in the second case. And 1 in the first (all 3 bindings pointing to the impl).

Dhanji.
Reply all
Reply to author
Forward
0 new messages