How can I bind annotated singletons of the same class to different instances?

309 views
Skip to first unread message

glenviewjeff

unread,
Aug 18, 2011, 1:26:51 PM8/18/11
to google...@googlegroups.com
Also posted on Stack Overflow

would like to bind a unique singleton instance for each annotation as shown below, such that Client1.a != Client1.b and Client1.a == Client2.a.
class X {}

@Singleton class OneOfEachAnnotation {
@Inject OneOfEachAnnotation(X x) { }
}

class Client1 {
@Inject Client(@A OneOfEachAnnotation a, @B OneOfEachAnnotation b) {}
}

class Client2 {
@Inject Client(@A OneOfEachAnnotation a, @B OneOfEachAnnotation b) {}
}

This answer seems to claim that I can accomplish the binding like this, but when I do so, Client1.a == Client1.b.

bind(OneOfEachAnnotation.class).annotatedWith(A.class).to(OneOfEachAnnotation.class).in(Singleton.class);
bind(SingletonClass.class).annotatedWith(B.class).to(SingletonClass.class).in(Singleton.class);

Do I have to create a provider class and expose the constructor of OneOfEachAnnotation just to accomplish this? I'll be very surprised if there isn't a simpler method for solving this common problem.


glenviewjeff

unread,
Aug 18, 2011, 1:28:39 PM8/18/11
to google...@googlegroups.com
Sorry, I made a typo in the binding lines.  They should read:
bind(OneOfEachAnnotation.class).annotatedWith(A.class).to(OneOfEachAnnotation.class).in(Singleton.class);
bind(OneOfEachAnnotation.class).annotatedWith(B.class).to(OneOfEachAnnotation.class).in(Singleton.class);

Sam Berlin

unread,
Aug 18, 2011, 1:41:03 PM8/18/11
to google...@googlegroups.com
This should work.  The only thing that could cause it to not work is if OneOfEachAnnotation has @Singleton in it, or if there's a separate binding statement of bind(OneOfEachAnnotation.class).in(Singleton.class).

sam


--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/XrKCkJP9QVEJ.

To post to this group, send email to google...@googlegroups.com.
To unsubscribe from this group, send email to google-guice...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-guice?hl=en.

glenviewjeff

unread,
Aug 18, 2011, 2:04:25 PM8/18/11
to google...@googlegroups.com
Thanks for pointing this out Sam.  The code in my question does have the @Singleton annotation on the OneOfEachAnnotation.  Removing this fixed the problem.

It's not entirely clear to me why this causes it to behave as a "single" singleton instead of respecting the annotation.  If you are able to help me understand why, I'd be grateful.

Sam Berlin

unread,
Aug 18, 2011, 2:13:03 PM8/18/11
to google...@googlegroups.com
The statements:

bind(OneOfEachAnnotation.class).annotatedWith(A.class).to(OneOfEachAnnotation.class).in(Singleton.class);
bind(OneOfEachAnnotation.class).annotatedWith(B.class).to(OneOfEachAnnotation.class).in(Singleton.class);

are saying, "Create a binding '@A OneOfEachAnnotation' that to OneOfEachAnnotation' and make the @A OneOfEachAnnotation a singleton.  Same thing with @B.

Ignoring the @Singleton on OneOfEachAnnotation for a second (assuming it didn't exist), then this would have the behavior of:
  User injects OneOfEachAnnotation (unannotated) multiple times: a new one is created each time, because that binding is unscoped.
  User injects @A OneOfEachAnnotation, the first time it links down to the unannotated OneOfEach, sees it needs to be constructed, and constructs it.  The second time the binding is already provisioned, so Scopes.SINGLETON doesn't go to the linked binding again.
   (Same thing with @B as @A.)

When OneOfEachAnnotation has @Singleton on it, then Guice thinks that the unscoped value is _also_ a singleton.  So when @A links to the unannotated, it will create the first instance.  When @B links down the unannotated, Guice notices that the unannotated version, also a Singleton, has already been constructed, and returns that instance.

(Hopefully that makes some sense.)

sam

On Thu, Aug 18, 2011 at 2:04 PM, glenviewjeff <glenvi...@gmail.com> wrote:
Thanks for pointing this out Sam.  The code in my question does have the @Singleton annotation on the OneOfEachAnnotation.  Removing this fixed the problem.

It's not entirely clear to me why this causes it to behave as a "single" singleton instead of respecting the annotation.  If you are able to help me understand why, I'd be grateful.

--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/vGTbS-WhFEUJ.

glenviewjeff

unread,
Aug 18, 2011, 2:45:26 PM8/18/11
to google...@googlegroups.com
Sam, thanks so much for the great (and understandable explanation.) 

It was, in fact, the case that I did wish the unannotated versions to be singletons as well, but to be a different singleton than the two annotated ones.  I now understand how and why this doesn't work with Guice, and it's really not a big deal to just create a third annotation and never inject unannotated OneOfEachAnnotation classes.

That having been said, I have a follow-up question if you don't mind:  Is there a mechanism to require annotations so I don't inadvertently do so?

Thanks,
Jeff

Sam Berlin

unread,
Aug 18, 2011, 2:49:42 PM8/18/11
to google...@googlegroups.com
You actually _can_ leave the unannotated version and still get what you want, but you need to delve a bit more into Guice's API and use the toConstructor binding.  The issue here is what's called the "Turkey Bacon" problem and is described @ http://tinyurl.com/turkeybaconproblem .  The fixed issue @ http://code.google.com/p/google-guice/issues/detail?id=231 describes it in a little detail.  Coment 8 in that issue should give some idea how to resolve this for yourself -- basically you bind OneOfEach as a singleton itself, bind @A OneOfEach using toConstructor(..).in(Singleton.class), and same with @B.  Doing it this way should avoid the need for the error checking (which I'm not sure would be possible without some fun use of the Guice SPI.)

sam


--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/wEXSWcM1hI4J.
Reply all
Reply to author
Forward
0 new messages