All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy