com.google.inject.internal.ConstructorInjector Maven / Gradle / Ivy
package com.google.inject.internal;
import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.InvocationTargetException;
import java.util.Set;
/**
* Creates instances using an injectable constructor. After construction, all injectable fields and
* methods are injected.
*/
final class ConstructorInjector {
private final ImmutableSet injectableMembers;
private final SingleParameterInjector>[] parameterInjectors;
private final ConstructionProxy constructionProxy;
private final MembersInjectorImpl membersInjector;
ConstructorInjector(Set injectableMembers,
ConstructionProxy constructionProxy,
SingleParameterInjector>[] parameterInjectors,
MembersInjectorImpl membersInjector) {
this.injectableMembers = ImmutableSet.copyOf(injectableMembers);
this.constructionProxy = constructionProxy;
this.parameterInjectors = parameterInjectors;
this.membersInjector = membersInjector;
}
public ImmutableSet getInjectableMembers() {
return injectableMembers;
}
ConstructionProxy getConstructionProxy() {
return constructionProxy;
}
/**
* Construct an instance. Returns {@code Object} instead of {@code T} because
* it may return a proxy.
*/
Object construct(final InternalContext context,
Dependency> dependency,
ProvisionListenerStackCallback provisionCallback)
throws InternalProvisionException {
final ConstructionContext constructionContext = context.getConstructionContext(this);
// We have a circular reference between constructors. Return a proxy.
if (constructionContext.isConstructing()) {
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
return constructionContext.createProxy(
context.getInjectorOptions(), dependency.getKey().getTypeLiteral().getRawType());
}
// If we're re-entering this factory while injecting fields or methods,
// return the same instance. This prevents infinite loops.
T t = constructionContext.getCurrentReference();
if (t != null) {
if (context.getInjectorOptions().disableCircularProxies) {
throw InternalProvisionException.circularDependenciesDisabled(
dependency.getKey().getTypeLiteral().getRawType());
} else {
return t;
}
}
constructionContext.startConstruction();
try {
// Optimization: Don't go through the callback stack if we have no listeners.
if (provisionCallback == null) {
return provision(context, constructionContext);
} else {
return provisionCallback.provision(context, () ->
provision(context, constructionContext));
}
} finally {
constructionContext.finishConstruction();
}
}
/**
* Provisions a new T.
*/
private T provision(InternalContext context, ConstructionContext constructionContext)
throws InternalProvisionException {
try {
T t;
try {
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
t = constructionProxy.newInstance(parameters);
constructionContext.setProxyDelegates(t);
} finally {
constructionContext.finishConstruction();
}
// Store reference. If an injector re-enters this factory, they'll get the same reference.
constructionContext.setCurrentReference(t);
MembersInjectorImpl localMembersInjector = membersInjector;
localMembersInjector.injectMembers(t, context, false);
localMembersInjector.notifyListeners(t);
return t;
} catch (InvocationTargetException userException) {
Throwable cause = userException.getCause() != null
? userException.getCause()
: userException;
throw InternalProvisionException.errorInjectingConstructor(cause)
.addSource(constructionProxy.getInjectionPoint());
} finally {
constructionContext.removeCurrentReference();
}
}
}