Skip to content

Instantly share code, notes, and snippets.

@eneveu
Created August 19, 2011 11:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eneveu/1156593 to your computer and use it in GitHub Desktop.
Save eneveu/1156593 to your computer and use it in GitHub Desktop.
How to bind multiple Guice singletons of the same type under different binding annotations
/**
* Copyright (C) 2011 Etienne Neveu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.headexplodes.example.guice;
import java.lang.annotation.Retention;
import javax.inject.Singleton;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.BindingAnnotation;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Scopes;
import org.testng.annotations.Test;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.testng.Assert.assertNotSame;
import static org.testng.Assert.assertSame;
/**
* Demo for
* http://stackoverflow.com/questions/1221920/getting-multiple-guice-singletons-of-the-same-type/1407719#1407719
* <p/>
* This demonstrates that you can bind multiple singletons of the same type under different
* binding annotations.
* <p/>
* As explained in the
* <a href="http://code.google.com/p/google-guice/wiki/Scopes">Guice Documentation</a>:
* <pre>
* In linked bindings, scopes apply to the binding source, not the binding target. Suppose
* we have a class Applebees that implements both Bar and Grill interfaces. These bindings
* allow for two instances of that type, one for Bars and another for Grills:
*
* bind(Bar.class).to(Applebees.class).in(Singleton.class);
* bind(Grill.class).to(Applebees.class).in(Singleton.class);
*
* This is because the scopes apply to the bound type (Bar, Grill), not the type that
* satisfies that binding (Applebees). To allow only a single instance to be created,
* use a @Singleton annotation on the declaration for that class. Or add another binding:
*
* bind(Applebees.class).in(Singleton.class);
*
* This binding makes the other two .in(Singleton.class) clauses above unnecessary.
* </pre>
*/
public class MultipleGuiceSingletonsTest {
@Retention(RUNTIME)
@BindingAnnotation
@interface One {}
@Retention(RUNTIME)
@BindingAnnotation
@interface Two {}
static class MySingleton {}
@Singleton
static class MyAnnotatedSingleton {}
/**
* {@code toInstance()} bindings obviously return different instances. But they have
* drawbacks.
* As explained in the {@link Binder} javadoc, they result in "eager loading" behavior
* that you can't control.
* Moreover, you can't use constructor injection for {@code toInstance()} bindings.
*/
@Test
public void testToInstanceBinding() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(MySingleton.class).annotatedWith(One.class)
.toInstance(new MySingleton());
bind(MySingleton.class).annotatedWith(Two.class)
.toInstance(new MySingleton());
}
});
assertNotSame(
injector.getInstance(Key.get(MySingleton.class, One.class)),
injector.getInstance(Key.get(MySingleton.class, Two.class))
);
}
/**
* As explained in this test's javadoc, scopes apply to the bound type, not the type
* that satisfies the binding.
* In this case, the bound types are {@code [MySingleton, @One]} and
* {@code [MySingleton, @Two]}, and Guice constructs two instances.
*/
@Test
public void testAnnotatedSingletonBinding_multipleInstances() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(MySingleton.class).annotatedWith(One.class)
.to(MySingleton.class).in(Singleton.class);
bind(MySingleton.class).annotatedWith(Two.class)
.to(MySingleton.class).in(Singleton.class);
}
});
assertNotSame(
injector.getInstance(Key.get(MySingleton.class, One.class)),
injector.getInstance(Key.get(MySingleton.class, Two.class))
);
}
/**
* We can force the creation of a single instance by adding a singleton binding.
*/
@Test
public void testAnnotatedSingletonBinding_singleInstance_singletonBinding() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(MySingleton.class).in(Scopes.SINGLETON);
bind(MySingleton.class).annotatedWith(One.class)
.to(MySingleton.class).in(Singleton.class);
bind(MySingleton.class).annotatedWith(Two.class)
.to(MySingleton.class).in(Singleton.class);
}
});
assertSame(
injector.getInstance(Key.get(MySingleton.class, One.class)),
injector.getInstance(Key.get(MySingleton.class, Two.class))
);
}
/**
* We can also force the creation of a single instance by annotating the bound class
* with {@code @Singleton}.
* <p/>
* <b>Notice that we use {@link MyAnnotatedSingleton} instead of {@link MySingleton}
* in this test.</b>
*/
@Test
public void testAnnotatedSingletonBinding_singleInstance_annotatedSingleton() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(MyAnnotatedSingleton.class).annotatedWith(One.class)
.to(MyAnnotatedSingleton.class).in(Singleton.class);
bind(MyAnnotatedSingleton.class).annotatedWith(Two.class)
.to(MyAnnotatedSingleton.class).in(Singleton.class);
}
});
assertSame(
injector.getInstance(Key.get(MyAnnotatedSingleton.class, One.class)),
injector.getInstance(Key.get(MyAnnotatedSingleton.class, Two.class))
);
}
/**
* We can still obtain multiple singletons when the bound class is annotated with
* {@code @Singleton}. To achieve this, we override {@code @Singleton} annotation
* by specifying the {@link Scopes#NO_SCOPE} in the Guice module.
* <p/>
* <b>Notice that we use {@link MyAnnotatedSingleton} instead of {@link MySingleton}
* in this test.</b>
*/
@Test
public void testAnnotatedSingletonBinding_multipleInstances_annotatedSingletonOverriddenInModule() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
// Override the @Singleton annotation to force Guice to create two singletons
bind(MyAnnotatedSingleton.class).in(Scopes.NO_SCOPE);
bind(MyAnnotatedSingleton.class).annotatedWith(One.class)
.to(MyAnnotatedSingleton.class).in(Singleton.class);
bind(MyAnnotatedSingleton.class).annotatedWith(Two.class)
.to(MyAnnotatedSingleton.class).in(Singleton.class);
}
});
assertNotSame(
injector.getInstance(Key.get(MyAnnotatedSingleton.class, One.class)),
injector.getInstance(Key.get(MyAnnotatedSingleton.class, Two.class))
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment