Skip to content

Instantly share code, notes, and snippets.

@gissuebot
Created July 7, 2014 17:53
Show Gist options
  • Save gissuebot/f4ebbcb259615c9327f6 to your computer and use it in GitHub Desktop.
Save gissuebot/f4ebbcb259615c9327f6 to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 38, comment 16
package org.limewire.inject;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import com.google.inject.Injector;
import com.google.inject.ScopeAnnotation;
/**
* Apply this to implementation classes when you want only one instance
* (per {@link Injector}) to be reused for all injections for that binding.
*
* The singleton is guaranteed to be constructed eagerly.
*/
@Target( { TYPE, METHOD })
@Retention(RUNTIME)
@ScopeAnnotation
public @interface EagerSingleton {
}
package org.limewire.inject;
import junit.framework.Test;
import org.limewire.util.BaseTestCase;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
public class EagerSingletonTest extends BaseTestCase {
private static boolean EAGER_ANNOTATED_CONSTRUCTED;
private static boolean FOO_CONSTRUCTED;
public EagerSingletonTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(EagerSingletonTest.class);
}
@Override
protected void setUp() throws Exception {
EAGER_ANNOTATED_CONSTRUCTED = false;
FOO_CONSTRUCTED = false;
}
public void testEagerAnnotated() throws Exception {
assertFalse(EAGER_ANNOTATED_CONSTRUCTED);
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
install(new LimeWireInjectModule());
bind(EagerAnnotated.class);
}
});
assertTrue(EAGER_ANNOTATED_CONSTRUCTED);
Provider<?> p = injector.getProvider(EagerAnnotated.class);
EagerAnnotated la = injector.getInstance(EagerAnnotated.class);
assertSame(la, injector.getInstance(EagerAnnotated.class));
assertSame(la, p.get());
}
public void testEagerBoundByClassAnnotation() throws Exception {
assertFalse(FOO_CONSTRUCTED);
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
install(new LimeWireInjectModule());
bind(Foo.class).in(EagerSingleton.class);
}
});
assertTrue(FOO_CONSTRUCTED);
Provider<?> p = injector.getProvider(Foo.class);
Foo foo = injector.getInstance(Foo.class);
assertSame(foo, injector.getInstance(Foo.class));
assertSame(foo, p.get());
}
public void testEagerBoundByScope() throws Exception {
assertFalse(FOO_CONSTRUCTED);
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
install(new LimeWireInjectModule());
bind(Foo.class).in(MoreScopes.EAGER_SINGLETON);
}
});
assertTrue(FOO_CONSTRUCTED);
Provider<?> p = injector.getProvider(Foo.class);
Foo foo = injector.getInstance(Foo.class);
assertSame(foo, injector.getInstance(Foo.class));
assertSame(foo, p.get());
}
public static void testJitFromContainerEager() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
install(new LimeWireInjectModule());
bind(Container.class);
}
});
// This fails because EagerAnnotated was injected as a JIT binding,
// and Injector provides no way of iterating over JIT bindings.
assertTrue(EAGER_ANNOTATED_CONSTRUCTED);
}
public static void testJitFromInjector() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
install(new LimeWireInjectModule());
}
});
// This fails because the TypeListener tries to create the object
// before the binding is ready.
Provider<EagerAnnotated> p = injector.getProvider(EagerAnnotated.class);
assertTrue(EAGER_ANNOTATED_CONSTRUCTED);
}
@EagerSingleton
private static class EagerAnnotated {
public EagerAnnotated() {
EAGER_ANNOTATED_CONSTRUCTED = true;
}
}
private static class Foo {
public Foo() {
FOO_CONSTRUCTED = true;
}
}
private static class Container {
@Inject Container(EagerAnnotated annotated) {
}
}
}
package org.limewire.inject;
import java.lang.annotation.Annotation;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.BindingScopingVisitor;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
public class LimeWireInjectModule extends AbstractModule {
@Override
protected void configure() {
bindScope(LazySingleton.class, MoreScopes.LAZY_SINGLETON);
bindScope(EagerSingleton.class, MoreScopes.EAGER_SINGLETON);
TypeListener listener = new EagerCreatingListener();
requestInjection(listener);
bindListener(Matchers.any(), listener);
}
private static class EagerCreatingListener implements TypeListener {
private Injector injector;
@Inject void injector(Injector injector) {
this.injector = injector;
for (Binding<?> b : injector.getBindings().values()) {
createIfEager(b);
}
}
@Override
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
if(injector != null) {
createIfEager(injector.getBinding(Key.get(type)));
}
}
private void createIfEager(final Binding<?> b) {
b.acceptScopingVisitor(new BindingScopingVisitor<Void>() {
@Override
public Void visitEagerSingleton() {
return null;
}
@Override
public Void visitNoScoping() {
return null;
}
@Override
public Void visitScope(Scope scope) {
if (scope == MoreScopes.EAGER_SINGLETON) {
b.getProvider().get();
}
return null;
}
@Override
public Void visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
return null;
}
});
}
}
}
package org.limewire.inject;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Scopes;
public class MoreScopes {
/**
* A singleton that will never be eager, in contrast to
* {@link Scopes#SINGLETON}, which Guice eagerly creates sometimes.
*/
public static final Scope LAZY_SINGLETON = new Scope() {
public <T> Provider<T> scope(Key<T> key, Provider<T> creator) {
return Scopes.SINGLETON.scope(key, creator);
}
@Override public String toString() {
return "MoreScopes.LAZY_SINGLETON";
}
};
/** A singleton that will make every attempt to always be eager. */
public static final Scope EAGER_SINGLETON = new Scope() {
public <T> Provider<T> scope(Key<T> key, Provider<T> creator) {
return Scopes.SINGLETON.scope(key, creator);
}
@Override public String toString() {
return "MoreScopes.EAGER_SINGLETON";
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment