Last active
April 24, 2023 14:16
-
-
Save sameb/56871be6994052ad2db8c007d5df2948 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |
import com.google.inject.Provides; | |
import com.google.inject.ScopeAnnotation; | |
import com.google.inject.Stage; | |
import com.google.inject.binder.ScopedBindingBuilder; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.Target; | |
/** | |
* A scope annotation that indicates the binding is a singleton that should be loaded eagerly. | |
* Used in conjunction with EagerSingletonModule. | |
*/ | |
@Target({ElementType.TYPE, ElementType.METHOD}) | |
@Retention(RUNTIME) | |
@ScopeAnnotation | |
public @interface EagerSingleton {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.Provider; | |
import com.google.inject.Provides; | |
import com.google.inject.Scope; | |
import com.google.inject.Scopes; | |
import com.google.inject.Singleton; | |
import com.google.inject.Stage; | |
import com.google.inject.binder.ScopedBindingBuilder; | |
import com.google.inject.spi.DefaultBindingScopingVisitor; | |
import java.lang.annotation.Annotation; | |
/** | |
* Binds the EagerSingleton scope annotation. It is just like Singleton, except the singleton loading | |
* is manually triggered. | |
* | |
* <p>Eager singletons must be loaded by injecting EagerSingletonLoader and calling loadEagerSingletons. | |
* | |
* <p>This is typically most useful as an annotation on {@literal @} {@link Provides} methods, as an | |
* easy way to simulate the behavior of {@link ScopedBindingBuilder#asEagerSingleton()}. It is also | |
* useful if your injector starts in {@link Stage#DEVELOPMENT} but you want some specific singletons | |
* to eagerly load (without using asEagerSingleton). Tests that do not call loadEagerSingletons | |
* will not load the eager singletons. | |
*/ | |
public final class EagerSingletonModule extends AbstractModule { | |
private static final Scope SCOPE = | |
new Scope() { | |
@Override | |
public <T> Provider<T> scope(Key<T> key, Provider<T> creator) { | |
return Scopes.SINGLETON.scope(key, creator); | |
} | |
@Override | |
public String toString() { | |
return "EagerSingleton"; | |
} | |
}; | |
/** Loads all things annotated with EagerSingleton. */ | |
@Singleton | |
public static class EagerSingletonLoader { | |
private final Injector injector; | |
@Inject | |
EagerSingletonLoader(Injector injector) { | |
this.injector = injector; | |
} | |
public void loadEagerSingletons() { | |
Visitor visitor = new Visitor(); | |
for (Binding<?> binding : injector.getAllBindings().values()) { | |
if (binding.acceptScopingVisitor(visitor)) { | |
binding.getProvider().get(); // construct it. | |
} | |
} | |
} | |
} | |
/** Returns whether the given binding is an eager singleton. */ | |
public static boolean isEagerSingleton(Binding<?> binding) { | |
return Scopes.isScoped(binding, SCOPE, EagerSingleton.class); | |
} | |
/** A simple visitor that returns true if the scope is EagerSingleton. */ | |
private static class Visitor extends DefaultBindingScopingVisitor<Boolean> { | |
@Override | |
public Boolean visitScope(Scope scope) { | |
return scope == SCOPE; | |
} | |
@Override | |
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) { | |
return scopeAnnotation == EagerSingleton.class; | |
} | |
@Override | |
protected Boolean visitOther() { | |
return false; | |
} | |
} | |
@Override | |
protected void configure() { | |
bindScope(EagerSingleton.class, SCOPE); | |
bind(EagerSingletonLoader.class); | |
} | |
/** Overridden so multiple of these modules can be installed without creating conflicting bindings. */ | |
@Override | |
public boolean equals(Object o) { | |
return o != null && this.getClass().equals(o.getClass()); | |
} | |
/** Overridden so multiple of these modules can be installed without creating conflicting bindings. */ | |
@Override | |
public int hashCode() { | |
return this.getClass().hashCode(); | |
} | |
/** | |
* <p>Overridden to emulate the expected behavior of {@link Object#toString} by using {@link | |
* System#identityHashCode}. The inhereted implementation will use {@link #hashCode}, which is the | |
* same across all instances and is likely not what users expect when they see a {@code | |
* Name@1234}-style {@code toString} output. | |
*/ | |
@Override | |
public String toString() { | |
return getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(this)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment