Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.inject.internal.InjectorImpl Maven / Gradle / Ivy
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException;
import com.google.inject.ImplementedBy;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.ProvidedBy;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConvertedConstantBinding;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.util.Providers;
import java.lang.annotation.Annotation;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Default {@link Injector} implementation.
*
*/
final class InjectorImpl implements Injector, Lookups {
public static final TypeLiteral STRING_TYPE = TypeLiteral.get(String.class);
final State state;
final InjectorImpl parent;
final ListMultimap, Binding>> bindingsMultimap = ArrayListMultimap.create();
final InjectorOptions options;
/**
* Just-in-time binding cache. Guarded by state.lock()
*/
final Map, BindingImpl>> jitBindings = Maps.newHashMap();
/**
* Cache of Keys that we were unable to create JIT bindings for, so we don't
* keep trying. Also guarded by state.lock().
*/
final Set> failedJitBindings = Sets.newHashSet();
Lookups lookups = new DeferredLookups(this);
/** The set of types passed to {@link #getMembersInjector} and {@link #injectMembers}. */
final Set> userRequestedMembersInjectorTypes = Sets.newConcurrentHashSet();
/**
* Cached constructor injectors for each type
*/
final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this);
private final ThreadLocal localContext;
/**
* Cached field and method injectors for each type.
*/
MembersInjectorStore membersInjectorStore;
/**
* Cached provision listener callbacks for each key.
*/
ProvisionListenerCallbackStore provisionListenerStore;
InjectorImpl(InjectorImpl parent, State state, InjectorOptions injectorOptions) {
this.parent = parent;
this.state = state;
this.options = injectorOptions;
if (parent != null) {
localContext = parent.localContext;
} else {
// No ThreadLocal.initialValue(), as that would cause classloader leaks. See
// https://github.com/google/guice/issues/288#issuecomment-48216933,
// https://github.com/google/guice/issues/288#issuecomment-48216944
localContext = new ThreadLocal<>();
}
}
/** Only to be called by the {@link SingletonScope} provider. */
InternalContext getLocalContext() {
return (InternalContext) localContext.get()[0];
}
InternalContext enterContext() {
Object[] reference = localContext.get();
if (reference == null) {
reference = new Object[1];
localContext.set(reference);
}
InternalContext ctx = (InternalContext) reference[0];
if (ctx == null) {
reference[0] = ctx = new InternalContext(options, reference);
} else {
ctx.enter();
}
return ctx;
}
/**
* Indexes bindings by type.
*/
void index() {
for (Binding> binding : state.getExplicitBindingsThisLevel().values()) {
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
}
}
@Override
public List> findBindingsByType(TypeLiteral type) {
@SuppressWarnings("unchecked") // safe because we only put matching entries into the map
List> list = (List>) (List) bindingsMultimap.get(checkNotNull(type, "type"));
return Collections.unmodifiableList(list);
}
/**
* Returns the binding for {@code key}
*/
@Override
public BindingImpl getBinding(Key key) {
Errors errors = new Errors(checkNotNull(key, "key"));
try {
BindingImpl result = getBindingOrThrow(key, errors, JitLimitation.EXISTING_JIT);
errors.throwConfigurationExceptionIfErrorsExist();
return result;
} catch (ErrorsException e) {
throw new ConfigurationException(errors.merge(e.getErrors()).getMessages());
}
}
@Override
public BindingImpl getExistingBinding(Key key) {
// Check explicit bindings, i.e. bindings created by modules.
BindingImpl explicitBinding = state.getExplicitBinding(key);
if (explicitBinding != null) {
return explicitBinding;
}
synchronized (state.lock()) {
// See if any jit bindings have been created for this key.
for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
@SuppressWarnings("unchecked")
BindingImpl jitBinding = (BindingImpl) injector.jitBindings.get(key);
if (jitBinding != null) {
return jitBinding;
}
}
}
// If Key is a Provider, we have to see if the type it is providing exists,
// and, if so, we have to create the binding for the provider.
if (isProvider(key)) {
try {
// This is safe because isProvider above ensures that T is a Provider>
@SuppressWarnings({"unchecked", "cast"})
Key> providedKey = (Key>) getProvidedKey((Key) key, new Errors());
if (getExistingBinding(providedKey) != null) {
return getBinding(key);
}
} catch (ErrorsException e) {
throw new ConfigurationException(e.getErrors().getMessages());
}
}
// No existing binding exists.
return null;
}
/**
* Gets a binding implementation. First, it check to see if the parent has a binding. If the
* parent has a binding and the binding is scoped, it will use that binding. Otherwise, this
* checks for an explicit binding. If no explicit binding is found, it looks for a just-in-time
* binding.
*/
BindingImpl getBindingOrThrow(Key key, Errors errors, JitLimitation jitType)
throws ErrorsException {
// Check explicit bindings, i.e. bindings created by modules.
BindingImpl binding = state.getExplicitBinding(key);
if (binding != null) {
return binding;
}
// Look for an on-demand binding.
return getJustInTimeBinding(key, errors, jitType);
}
@Override
public Binding getBinding(Class type) {
return getBinding(Key.get(checkNotNull(type, "type")));
}
@Override
public Injector getParent() {
return parent;
}
@Override
public Injector createChildInjector(Iterable extends Module> modules) {
return new InternalInjectorCreator().parentInjector(this).addModules(modules).build();
}
@Override
public Injector createChildInjector(Module... modules) {
return createChildInjector(ImmutableList.copyOf(modules));
}
/**
* Returns a just-in-time binding for {@code key}, creating it if necessary.
*
* @throws ErrorsException if the binding could not be created.
*/
private BindingImpl getJustInTimeBinding(Key key, Errors errors, JitLimitation jitType)
throws ErrorsException {
boolean jitOverride = isProvider(key) || isTypeLiteral(key) || isMembersInjector(key);
synchronized (state.lock()) {
// first try to find a JIT binding that we've already created
for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
@SuppressWarnings("unchecked") // we only store bindings that match their key
BindingImpl binding = (BindingImpl) injector.jitBindings.get(key);
if (binding != null) {
// If we found a JIT binding and we don't allow them,
// fail. (But allow bindings created through TypeConverters.)
if (options.jitDisabled
&& jitType == JitLimitation.NO_JIT
&& !jitOverride
&& !(binding instanceof ConvertedConstantBindingImpl)) {
throw errors.jitDisabled(key).toException();
} else {
return binding;
}
}
}
// If we previously failed creating this JIT binding and our Errors has
// already recorded an error, then just directly throw that error.
// We need to do this because it's possible we already cleaned up the
// entry in jitBindings (during cleanup), and we may be trying
// to create it again (in the case of a recursive JIT binding).
// We need both of these guards for different reasons
// failedJitBindings.contains: We want to continue processing if we've never
// failed before, so that our initial error message contains
// as much useful information as possible about what errors exist.
// errors.hasErrors: If we haven't already failed, then it's OK to
// continue processing, to make sure the ultimate error message
// is the correct one.
// See: ImplicitBindingsTest#testRecursiveJitBindingsCleanupCorrectly
// for where this guard compes into play.
if (failedJitBindings.contains(key) && errors.hasErrors()) {
throw errors.toException();
}
return createJustInTimeBindingRecursive(key, errors, options.jitDisabled, jitType);
} // end synchronized(state.lock())
}
/**
* Returns true if the key type is Provider (but not a subclass of Provider).
*/
private static boolean isProvider(Key> key) {
return key.getTypeLiteral().getRawType().equals(Provider.class);
}
private static boolean isTypeLiteral(Key> key) {
return key.getTypeLiteral().getRawType().equals(TypeLiteral.class);
}
private static Key getProvidedKey(Key> key, Errors errors)
throws ErrorsException {
Type providerType = key.getTypeLiteral().getType();
// If the Provider has no type parameter (raw Provider)...
if (!(providerType instanceof ParameterizedType)) {
throw errors.cannotInjectRawProvider().toException();
}
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0];
@SuppressWarnings("unchecked") // safe because T came from Key>
Key providedKey = (Key) key.ofType(entryType);
return providedKey;
}
/** Returns true if the key type is MembersInjector (but not a subclass of MembersInjector). */
private static boolean isMembersInjector(Key> key) {
return key.getTypeLiteral().getRawType().equals(MembersInjector.class)
&& key.getAnnotationType() == null;
}
private BindingImpl> createMembersInjectorBinding(
Key> key, Errors errors) throws ErrorsException {
Type membersInjectorType = key.getTypeLiteral().getType();
if (!(membersInjectorType instanceof ParameterizedType)) {
throw errors.cannotInjectRawMembersInjector().toException();
}
@SuppressWarnings("unchecked") // safe because T came from Key>
TypeLiteral instanceType = (TypeLiteral) TypeLiteral.get(
((ParameterizedType) membersInjectorType).getActualTypeArguments()[0]);
MembersInjector membersInjector = membersInjectorStore.get(instanceType, errors);
InternalFactory> factory =
new ConstantFactory<>(Initializables.of(membersInjector));
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
factory, ImmutableSet.of(), membersInjector);
}
/**
* Creates a synthetic binding to {@code Provider}, i.e. a binding to the provider from
* {@code Binding}.
*/
private BindingImpl> createProviderBinding(Key> key, Errors errors)
throws ErrorsException {
Key providedKey = getProvidedKey(key, errors);
BindingImpl delegate = getBindingOrThrow(providedKey, errors, JitLimitation.NO_JIT);
return new ProviderBindingImpl(this, key, delegate);
}
/**
* Converts a constant string binding to the required type.
*
* @return the binding if it could be resolved, or null if the binding doesn't exist
* @throws com.google.inject.internal.ErrorsException if there was an error resolving the binding
*/
private BindingImpl convertConstantStringBinding(Key key, Errors errors)
throws ErrorsException {
// Find a constant string binding.
Key stringKey = key.ofType(STRING_TYPE);
BindingImpl stringBinding = state.getExplicitBinding(stringKey);
if (stringBinding == null || !stringBinding.isConstant()) {
return null;
}
// We can't call getProvider().get() because this InstanceBinding may not have been inintialized
// yet (because we may have been called during InternalInjectorCreator.initializeStatically and
// instance binding validation hasn't happened yet.)
@SuppressWarnings("unchecked")
String stringValue = ((InstanceBinding) stringBinding).getInstance();
Object source = stringBinding.getSource();
// Find a matching type converter.
TypeLiteral type = key.getTypeLiteral();
TypeConverterBinding typeConverterBinding = state.getConverter(stringValue, type, errors, source);
if (typeConverterBinding == null) {
// No converter can handle the given type.
return null;
}
// Try to convert the string. A failed conversion results in an error.
try {
@SuppressWarnings("unchecked") // This cast is safe because we double check below.
T converted = (T) typeConverterBinding.getTypeConverter().convert(stringValue, type);
if (converted == null) {
throw errors.converterReturnedNull(stringValue, source, type, typeConverterBinding)
.toException();
}
if (!type.getRawType().isInstance(converted)) {
throw errors.conversionTypeError(stringValue, source, type, typeConverterBinding, converted)
.toException();
}
return new ConvertedConstantBindingImpl(this, key, converted, stringBinding,
typeConverterBinding);
} catch (RuntimeException e) {
throw errors.conversionError(stringValue, source, type, typeConverterBinding, e)
.toException();
}
}
void initializeBinding(BindingImpl binding, Errors errors) throws ErrorsException {
if (binding instanceof DelayedInitialize) {
((DelayedInitialize) binding).initialize(this, errors);
}
}
void initializeJitBinding(BindingImpl binding, Errors errors) throws ErrorsException {
// Put the partially constructed binding in the map a little early. This enables us to handle
// circular dependencies. Example: FooImpl -> BarImpl -> FooImpl.
// Note: We don't need to synchronize on state.lock() during injector creation.
if (binding instanceof DelayedInitialize) {
Key key = binding.getKey();
jitBindings.put(key, binding);
boolean successful = false;
DelayedInitialize delayed = (DelayedInitialize) binding;
try {
delayed.initialize(this, errors);
successful = true;
} finally {
if (!successful) {
// We do not pass cb.getInternalConstructor as the second parameter
// so that cached exceptions while constructing it get stored.
// See TypeListenerTest#testTypeListenerThrows
removeFailedJitBinding(binding, null);
cleanup(binding, new HashSet<>());
}
}
}
}
/**
* Iterates through the binding's dependencies to clean up any stray bindings that were leftover
* from a failed JIT binding. This is required because the bindings are eagerly &
* optimistically added to allow circular dependency support, so dependencies may pass where they
* should have failed.
*/
private boolean cleanup(BindingImpl> binding, Set> encountered) {
boolean bindingFailed = false;
Set> deps = getInternalDependencies(binding);
for (Dependency> dep : deps) {
Key> depKey = dep.getKey();
InjectionPoint ip = dep.getInjectionPoint();
if (encountered.add(depKey)) { // only check if we haven't looked at this key yet
BindingImpl> depBinding = jitBindings.get(depKey);
if (depBinding != null) { // if the binding still exists, validate
boolean failed = cleanup(depBinding, encountered); // if children fail, we fail
if (depBinding instanceof ConstructorBindingImpl) {
ConstructorBindingImpl> ctorBinding = (ConstructorBindingImpl) depBinding;
ip = ctorBinding.getInternalConstructor();
if (!ctorBinding.isInitialized()) {
failed = true;
}
}
if (failed) {
removeFailedJitBinding(depBinding, ip);
bindingFailed = true;
}
} else if (state.getExplicitBinding(depKey) == null) {
// ignore keys if they were explicitly bound, but if neither JIT
// nor explicit, it's also invalid & should let parent know.
bindingFailed = true;
}
}
}
return bindingFailed;
}
/**
* Cleans up any state that may have been cached when constructing the JIT binding.
*/
private void removeFailedJitBinding(Binding> binding, InjectionPoint ip) {
failedJitBindings.add(binding.getKey());
jitBindings.remove(binding.getKey());
membersInjectorStore.remove(binding.getKey().getTypeLiteral());
provisionListenerStore.remove(binding);
if (ip != null) {
constructors.remove(ip);
}
}
/**
* Safely gets the dependencies of possibly not initialized bindings.
*/
@SuppressWarnings("unchecked")
private Set> getInternalDependencies(BindingImpl> binding) {
if (binding instanceof ConstructorBindingImpl) {
return ((ConstructorBindingImpl) binding).getInternalDependencies();
} else if (binding instanceof HasDependencies) {
return ((HasDependencies) binding).getDependencies();
} else {
return ImmutableSet.of();
}
}
/**
* Creates a binding for an injectable type with the given scope. Looks for a scope on the type if
* none is specified.
*/
BindingImpl createUninitializedBinding(Key key, Scoping scoping, Object source,
Errors errors, boolean jitBinding) throws ErrorsException {
Class> rawType = key.getTypeLiteral().getRawType();
ImplementedBy implementedBy = rawType.getAnnotation(ImplementedBy.class);
// Don't try to inject arrays or enums annotated with @ImplementedBy.
if (rawType.isArray() || (rawType.isEnum() && implementedBy != null)) {
throw errors.missingImplementationWithHint(key, this).toException();
}
// Handle TypeLiteral by binding the inner type
if (rawType == TypeLiteral.class) {
@SuppressWarnings("unchecked") // we have to fudge the inner type as Object
BindingImpl binding = (BindingImpl) createTypeLiteralBinding(
(Key>) key, errors);
return binding;
}
// Handle @ImplementedBy
if (implementedBy != null) {
Annotations.checkForMisplacedScopeAnnotations(rawType, source, errors);
return createImplementedByBinding(key, scoping, implementedBy, errors);
}
// Handle @ProvidedBy.
ProvidedBy providedBy = rawType.getAnnotation(ProvidedBy.class);
if (providedBy != null) {
Annotations.checkForMisplacedScopeAnnotations(rawType, source, errors);
return createProvidedByBinding(key, scoping, providedBy, errors);
}
return ConstructorBindingImpl.create(this,
key,
null, /* use default constructor */
source,
scoping,
errors,
jitBinding && options.jitDisabled,
options.atInjectRequired);
}
/**
* Converts a binding for a {@code Key>} to the value {@code TypeLiteral}. It's
* a bit awkward because we have to pull out the inner type in the type literal.
*/
private BindingImpl> createTypeLiteralBinding(
Key> key, Errors errors) throws ErrorsException {
Type typeLiteralType = key.getTypeLiteral().getType();
if (!(typeLiteralType instanceof ParameterizedType)) {
throw errors.cannotInjectRawTypeLiteral().toException();
}
ParameterizedType parameterizedType = (ParameterizedType) typeLiteralType;
Type innerType = parameterizedType.getActualTypeArguments()[0];
// this is unforunate. We don't support building TypeLiterals for type variable like 'T'. If
// this proves problematic, we can probably fix TypeLiteral to support type variables
if (!(innerType instanceof Class)
&& !(innerType instanceof GenericArrayType)
&& !(innerType instanceof ParameterizedType)) {
throw errors.cannotInjectTypeLiteralOf(innerType).toException();
}
@SuppressWarnings("unchecked") // by definition, innerType == T, so this is safe
TypeLiteral value = (TypeLiteral) TypeLiteral.get(innerType);
InternalFactory> factory = new ConstantFactory<>(Initializables.of(value));
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
factory, ImmutableSet.of(), value);
}
/**
* Creates a binding for a type annotated with @ProvidedBy.
*/
BindingImpl createProvidedByBinding(Key key, Scoping scoping,
ProvidedBy providedBy, Errors errors) throws ErrorsException {
Class> rawType = key.getTypeLiteral().getRawType();
Class extends javax.inject.Provider>> providerType = providedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper loops?
if (providerType == rawType) {
throw errors.recursiveProviderType().toException();
}
// Assume the provider provides an appropriate type. We double check at runtime.
@SuppressWarnings("unchecked")
Key extends Provider> providerKey = (Key extends Provider>) Key.get(providerType);
ProvidedByInternalFactory internalFactory =
new ProvidedByInternalFactory(rawType, providerType, providerKey);
BindingImpl binding = LinkedProviderBindingImpl.createWithInitializer(
this,
key,
rawType,
Scoping.scope(key, this, internalFactory, rawType, scoping),
scoping,
providerKey,
internalFactory);
internalFactory.setProvisionListenerCallback(provisionListenerStore.get(binding));
return binding;
}
/**
* Creates a binding for a type annotated with @ImplementedBy.
*/
private BindingImpl createImplementedByBinding(Key key, Scoping scoping,
ImplementedBy implementedBy, Errors errors)
throws ErrorsException {
Class> rawType = key.getTypeLiteral().getRawType();
Class> implementationType = implementedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper cycles?
if (implementationType == rawType) {
throw errors.recursiveImplementationType().toException();
}
// Make sure implementationType extends type.
if (!rawType.isAssignableFrom(implementationType)) {
throw errors.notASubtype(implementationType, rawType).toException();
}
@SuppressWarnings("unchecked") // After the preceding check, this cast is safe.
Class extends T> subclass = (Class extends T>) implementationType;
// Look up the target binding.
final Key extends T> targetKey = Key.get(subclass);
FactoryProxy factory = new FactoryProxy<>(this, key, targetKey, rawType);
factory.notify(errors); // causes the factory to initialize itself internally
return new LinkedBindingImpl(
this,
key,
rawType,
Scoping.scope(key, this, factory, rawType, scoping),
scoping,
targetKey);
}
/**
* Attempts to create a just-in-time binding for {@code key} in the root injector, falling back to
* other ancestor injectors until this injector is tried.
*/
private BindingImpl createJustInTimeBindingRecursive(Key key, Errors errors,
boolean jitDisabled, JitLimitation jitType) throws ErrorsException {
// ask the parent to create the JIT binding
if (parent != null) {
if (jitType == JitLimitation.NEW_OR_EXISTING_JIT
&& jitDisabled && !parent.options.jitDisabled) {
// If the binding would be forbidden here but allowed in a parent, report an error instead
throw errors.jitDisabledInParent(key).toException();
}
try {
return parent.createJustInTimeBindingRecursive(key, new Errors(), jitDisabled,
parent.options.jitDisabled ? JitLimitation.NO_JIT : jitType);
} catch (ErrorsException ignored) {
}
}
// Retrieve the sources before checking for blacklisting to guard against sources becoming null
// due to a full GC happening after calling state.isBlacklisted and
// state.getSourcesForBlacklistedKey.
// TODO(user): Consolidate these two APIs.
Set sources = state.getSourcesForBlacklistedKey(key);
if (state.isBlacklisted(key)) {
throw errors.childBindingAlreadySet(key, sources).toException();
}
key = MoreTypes.canonicalizeKey(key); // before storing the key long-term, canonicalize it.
BindingImpl binding = createJustInTimeBinding(key, errors, jitDisabled, jitType);
state.parent().blacklist(key, state, binding.getSource());
jitBindings.put(key, binding);
return binding;
}
/**
* Returns a new just-in-time binding created by resolving {@code key}. The strategies used to
* create just-in-time bindings are:
*
* Internalizing Providers. If the requested binding is for {@code Provider}, we delegate
* to the binding for {@code T}.
* Converting constants.
* ImplementedBy and ProvidedBy annotations. Only for unannotated keys.
* The constructor of the raw type. Only for unannotated keys.
*
*
* @throws com.google.inject.internal.ErrorsException if the binding cannot be created.
*/
private BindingImpl createJustInTimeBinding(Key key, Errors errors,
boolean jitDisabled, JitLimitation jitType) throws ErrorsException {
int numErrorsBefore = errors.size();
// Retrieve the sources before checking for blacklisting to guard against sources becoming null
// due to a full GC happening after calling state.isBlacklisted and
// state.getSourcesForBlacklistedKey.
// TODO(user): Consolidate these two APIs.
Set sources = state.getSourcesForBlacklistedKey(key);
if (state.isBlacklisted(key)) {
throw errors.childBindingAlreadySet(key, sources).toException();
}
// Handle cases where T is a Provider>.
if (isProvider(key)) {
// These casts are safe. We know T extends Provider and that given Key>,
// createProviderBinding() will return BindingImpl>.
@SuppressWarnings({"unchecked", "cast"})
BindingImpl binding = (BindingImpl) createProviderBinding((Key) key, errors);
return binding;
}
// Handle cases where T is a MembersInjector>
if (isMembersInjector(key)) {
// These casts are safe. T extends MembersInjector and that given Key>,
// createMembersInjectorBinding() will return BindingImpl>.
@SuppressWarnings({"unchecked", "cast"})
BindingImpl binding = (BindingImpl) createMembersInjectorBinding((Key) key, errors);
return binding;
}
// Try to convert a constant string binding to the requested type.
BindingImpl convertedBinding = convertConstantStringBinding(key, errors);
if (convertedBinding != null) {
return convertedBinding;
}
if (!isTypeLiteral(key) && jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
throw errors.jitDisabled(key).toException();
}
// If the key has an annotation...
if (key.getAnnotationType() != null) {
// Look for a binding without annotation attributes or return null.
if (key.hasAttributes() && !options.exactBindingAnnotationsRequired) {
try {
Errors ignored = new Errors();
return getBindingOrThrow(key.withoutAttributes(), ignored, JitLimitation.NO_JIT);
} catch (ErrorsException ignored) {
// throw with a more appropriate message below
}
}
throw errors.missingImplementation(key).toException();
}
Object source = key.getTypeLiteral().getRawType();
BindingImpl binding = createUninitializedBinding(key, Scoping.UNSCOPED, source, errors, true);
errors.throwIfNewErrors(numErrorsBefore);
initializeJitBinding(binding, errors);
return binding;
}
InternalFactory extends T> getInternalFactory(Key key, Errors errors, JitLimitation jitType)
throws ErrorsException {
return getBindingOrThrow(key, errors, jitType).getInternalFactory();
}
@Override
public Map, Binding>> getBindings() {
return state.getExplicitBindingsThisLevel();
}
@Override
public Map, Binding>> getAllBindings() {
synchronized (state.lock()) {
return new ImmutableMap.Builder, Binding>>()
.putAll(state.getExplicitBindingsThisLevel())
.putAll(jitBindings)
.build();
}
}
@Override
public Map, Scope> getScopeBindings() {
return ImmutableMap.copyOf(state.getScopes());
}
@Override
public Set getTypeConverterBindings() {
return ImmutableSet.copyOf(state.getConvertersThisLevel());
}
@Override
public List getElements() {
ImmutableList.Builder elements = ImmutableList.builder();
elements.addAll(getAllBindings().values());
elements.addAll(state.getProviderLookupsThisLevel());
elements.addAll(state.getConvertersThisLevel());
elements.addAll(state.getScopeBindingsThisLevel());
elements.addAll(state.getTypeListenerBindingsThisLevel());
elements.addAll(state.getProvisionListenerBindingsThisLevel());
elements.addAll(state.getScannerBindingsThisLevel());
elements.addAll(state.getStaticInjectionRequestsThisLevel());
elements.addAll(state.getMembersInjectorLookupsThisLevel());
elements.addAll(state.getInjectionRequestsThisLevel());
return elements.build();
}
@SuppressWarnings("unchecked")
@Override
public Map, List> getAllMembersInjectorInjectionPoints() {
// Note, this is a safe cast per the ListMultimap javadocs.
// We could use Multimaps.asMap to avoid the cast, but unfortunately it's a @Beta method.
return (Map, List>)
(Map, ?>)
ImmutableListMultimap.copyOf(
Multimaps.filterKeys(
membersInjectorStore.getAllInjectionPoints(),
userRequestedMembersInjectorTypes::contains))
.asMap();
}
/**
* Returns parameter injectors, or {@code null} if there are no parameters.
*/
SingleParameterInjector>[] getParametersInjectors(
List> parameters, Errors errors) throws ErrorsException {
if (parameters.isEmpty()) {
return null;
}
int numErrorsBefore = errors.size();
SingleParameterInjector>[] result = new SingleParameterInjector>[parameters.size()];
int i = 0;
for (Dependency> parameter : parameters) {
try {
result[i++] = createParameterInjector(parameter, errors.withSource(parameter));
} catch (ErrorsException rethrownBelow) {
// rethrown below
}
}
errors.throwIfNewErrors(numErrorsBefore);
return result;
}
SingleParameterInjector createParameterInjector(final Dependency dependency,
final Errors errors) throws ErrorsException {
BindingImpl extends T> binding = getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
return new SingleParameterInjector(dependency, binding);
}
@SuppressWarnings({"unchecked","rawtypes"}) // the members injector type is consistent with instance's type
@Override
public void injectMembers(Object instance) {
MembersInjector membersInjector = getMembersInjector(instance.getClass());
membersInjector.injectMembers(instance);
}
@Override
public MembersInjector getMembersInjector(TypeLiteral typeLiteral) {
checkNotNull(typeLiteral, "typeLiteral");
userRequestedMembersInjectorTypes.add(typeLiteral);
Errors errors = new Errors(typeLiteral);
try {
return membersInjectorStore.get(typeLiteral, errors);
} catch (ErrorsException e) {
throw new ConfigurationException(errors.merge(e.getErrors()).getMessages());
}
}
@Override
public MembersInjector getMembersInjector(Class type) {
return getMembersInjector(TypeLiteral.get(type));
}
@Override
public Provider getProvider(Class type) {
return getProvider(Key.get(checkNotNull(type, "type")));
}
Provider getProviderOrThrow(final Dependency dependency, Errors errors) throws ErrorsException {
final Key key = dependency.getKey();
final BindingImpl extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
final InternalFactory extends T> internalFactory = binding.getInternalFactory();
final Object source = binding.getSource();
return new Provider<>() {
public T get() {
InternalContext currentContext = enterContext();
Dependency> previous = currentContext.pushDependency(dependency, source);
try {
return internalFactory.get(currentContext, dependency, false);
} catch (InternalProvisionException e) {
throw e.addSource(dependency).toProvisionException();
} finally {
currentContext.popStateAndSetDependency(previous);
currentContext.close();
}
}
@Override
public String toString() {
return internalFactory.toString();
}
};
}
@Override
public Provider getProvider(final Key key) {
checkNotNull(key, "key");
Errors errors = new Errors(key);
try {
Provider result = getProviderOrThrow(Dependency.get(key), errors);
errors.throwIfNewErrors(0);
return result;
} catch (ErrorsException e) {
throw new ConfigurationException(errors.merge(e.getErrors()).getMessages());
}
}
@Override
public T getInstance(Key key) {
return getProvider(key).get();
}
@Override
public T getInstance(Class type) {
return getProvider(type).get();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(Injector.class)
.add("bindings", state.getExplicitBindingsThisLevel().values())
.toString();
}
/**
* some limitations on what just in time bindings are allowed.
*/
enum JitLimitation {
/**
* does not allow just in time bindings
*/
NO_JIT,
/**
* allows existing just in time bindings, but does not allow new ones
*/
EXISTING_JIT,
/**
* allows existing just in time bindings & allows new ones to be created
*/
NEW_OR_EXISTING_JIT,
}
/**
* Invokes a method.
*/
interface MethodInvoker {
Object invoke(Object target, Object... parameters)
throws IllegalAccessException, InvocationTargetException;
}
/**
* Options that control how the injector behaves.
*/
static class InjectorOptions {
final Stage stage;
final boolean jitDisabled;
final boolean disableCircularProxies;
final boolean atInjectRequired;
final boolean exactBindingAnnotationsRequired;
InjectorOptions(Stage stage, boolean jitDisabled, boolean disableCircularProxies,
boolean atInjectRequired, boolean exactBindingAnnotationsRequired) {
this.stage = stage;
this.jitDisabled = jitDisabled;
this.disableCircularProxies = disableCircularProxies;
this.atInjectRequired = atInjectRequired;
this.exactBindingAnnotationsRequired = exactBindingAnnotationsRequired;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("stage", stage)
.add("jitDisabled", jitDisabled)
.add("disableCircularProxies", disableCircularProxies)
.add("atInjectRequired", atInjectRequired)
.add("exactBindingAnnotationsRequired", exactBindingAnnotationsRequired)
.toString();
}
}
private static class ProviderBindingImpl extends BindingImpl>
implements ProviderBinding>, HasDependencies {
final BindingImpl providedBinding;
ProviderBindingImpl(InjectorImpl injector, Key> key, Binding providedBinding) {
super(injector, key, providedBinding.getSource(), createInternalFactory(providedBinding),
Scoping.UNSCOPED);
this.providedBinding = (BindingImpl) providedBinding;
}
static InternalFactory> createInternalFactory(Binding providedBinding) {
final Provider provider = providedBinding.getProvider();
return (context, dependency, linked) -> provider;
}
@Override
public Key extends T> getProvidedKey() {
return providedBinding.getKey();
}
@Override
public V acceptTargetVisitor(BindingTargetVisitor super Provider, V> visitor) {
return visitor.visit(this);
}
@Override
public void applyTo(Binder binder) {
throw new UnsupportedOperationException("This element represents a synthetic binding.");
}
@Override
public String toString() {
return MoreObjects.toStringHelper(ProviderBinding.class)
.add("key", getKey())
.add("providedKey", getProvidedKey())
.toString();
}
@Override
public Set> getDependencies() {
return ImmutableSet.>of(Dependency.get(getProvidedKey()));
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ProviderBindingImpl) {
ProviderBindingImpl> o = (ProviderBindingImpl>) obj;
return getKey().equals(o.getKey())
&& getScoping().equals(o.getScoping())
&& Objects.equal(providedBinding, o.providedBinding);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getKey(), getScoping(), providedBinding);
}
}
private static class ConvertedConstantBindingImpl
extends BindingImpl implements ConvertedConstantBinding {
final T value;
final Provider provider;
final Binding originalBinding;
final TypeConverterBinding typeConverterBinding;
ConvertedConstantBindingImpl(InjectorImpl injector,
Key key,
T value,
Binding originalBinding,
TypeConverterBinding typeConverterBinding) {
super(injector, key, originalBinding.getSource(),
new ConstantFactory(Initializables.of(value)), Scoping.UNSCOPED);
this.value = value;
provider = Providers.of(value);
this.originalBinding = originalBinding;
this.typeConverterBinding = typeConverterBinding;
}
@Override
public Provider getProvider() {
return provider;
}
@Override
public V acceptTargetVisitor(BindingTargetVisitor super T, V> visitor) {
return visitor.visit(this);
}
@Override
public T getValue() {
return value;
}
@Override
public TypeConverterBinding getTypeConverterBinding() {
return typeConverterBinding;
}
@Override
public Key getSourceKey() {
return originalBinding.getKey();
}
@Override
public Set> getDependencies() {
return ImmutableSet.>of(Dependency.get(getSourceKey()));
}
@Override
public void applyTo(Binder binder) {
throw new UnsupportedOperationException("This element represents a synthetic binding.");
}
@Override
public String toString() {
return MoreObjects.toStringHelper(ConvertedConstantBinding.class)
.add("key", getKey())
.add("sourceKey", getSourceKey())
.add("value", value)
.toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ConvertedConstantBindingImpl) {
ConvertedConstantBindingImpl> o = (ConvertedConstantBindingImpl>) obj;
return getKey().equals(o.getKey())
&& getScoping().equals(o.getScoping())
&& Objects.equal(value, o.value);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getKey(), getScoping(), value);
}
}
}