Example usage for org.springframework.beans.factory.support AutowireUtils resolveReturnTypeForFactoryMethod

List of usage examples for org.springframework.beans.factory.support AutowireUtils resolveReturnTypeForFactoryMethod

Introduction

In this page you can find the example usage for org.springframework.beans.factory.support AutowireUtils resolveReturnTypeForFactoryMethod.

Prototype

public static Class<?> resolveReturnTypeForFactoryMethod(Method method, Object[] args,
        @Nullable ClassLoader classLoader) 

Source Link

Document

Determine the target type for the generic return type of the given generic factory method, where formal type variables are declared on the given method itself.

Usage

From source file:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.java

/**
 * Determine the target type for the given bean definition which is based on
 * a factory method. Only called if there is no singleton instance registered
 * for the target bean already./* w  w w .  j  av a 2 s .c  om*/
 * <p>This implementation determines the type matching {@link #createBean}'s
 * different creation strategies. As far as possible, we'll perform static
 * type checking to avoid creation of the target bean.
 * @param beanName the name of the bean (for error handling purposes)
 * @param mbd the merged bean definition for the bean
 * @param typesToMatch the types to match in case of internal type matching purposes
 * (also signals that the returned {@code Class} will never be exposed to application code)
 * @return the type for the bean if determinable, or {@code null} otherwise
 * @see #createBean
 */
@Nullable
protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
    ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
    if (cachedReturnType != null) {
        return cachedReturnType.resolve();
    }

    Class<?> factoryClass;
    boolean isStatic = true;

    String factoryBeanName = mbd.getFactoryBeanName();
    if (factoryBeanName != null) {
        if (factoryBeanName.equals(beanName)) {
            throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                    "factory-bean reference points back to the same bean definition");
        }
        // Check declared factory method return type on factory class.
        factoryClass = getType(factoryBeanName);
        isStatic = false;
    } else {
        // Check declared factory method return type on bean class.
        factoryClass = resolveBeanClass(mbd, beanName, typesToMatch);
    }

    if (factoryClass == null) {
        return null;
    }
    factoryClass = ClassUtils.getUserClass(factoryClass);

    // If all factory methods have the same return type, return that type.
    // Can't clearly figure out exact method due to type converting / autowiring!
    Class<?> commonType = null;
    Method uniqueCandidate = null;
    int minNrOfArgs = (mbd.hasConstructorArgumentValues()
            ? mbd.getConstructorArgumentValues().getArgumentCount()
            : 0);
    Method[] candidates = this.factoryMethodCandidateCache.computeIfAbsent(factoryClass,
            ReflectionUtils::getUniqueDeclaredMethods);

    for (Method candidate : candidates) {
        if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)
                && candidate.getParameterCount() >= minNrOfArgs) {
            // Declared type variables to inspect?
            if (candidate.getTypeParameters().length > 0) {
                try {
                    // Fully resolve parameter names and argument values.
                    Class<?>[] paramTypes = candidate.getParameterTypes();
                    String[] paramNames = null;
                    ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
                    if (pnd != null) {
                        paramNames = pnd.getParameterNames(candidate);
                    }
                    ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
                    Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(
                            paramTypes.length);
                    Object[] args = new Object[paramTypes.length];
                    for (int i = 0; i < args.length; i++) {
                        ConstructorArgumentValues.ValueHolder valueHolder = cav.getArgumentValue(i,
                                paramTypes[i], (paramNames != null ? paramNames[i] : null), usedValueHolders);
                        if (valueHolder == null) {
                            valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
                        }
                        if (valueHolder != null) {
                            args[i] = valueHolder.getValue();
                            usedValueHolders.add(valueHolder);
                        }
                    }
                    Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(candidate, args,
                            getBeanClassLoader());
                    uniqueCandidate = (commonType == null && returnType == candidate.getReturnType() ? candidate
                            : null);
                    commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
                    if (commonType == null) {
                        // Ambiguous return types found: return null to indicate "not determinable".
                        return null;
                    }
                } catch (Throwable ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Failed to resolve generic return type for factory method: " + ex);
                    }
                }
            } else {
                uniqueCandidate = (commonType == null ? candidate : null);
                commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
                if (commonType == null) {
                    // Ambiguous return types found: return null to indicate "not determinable".
                    return null;
                }
            }
        }
    }

    if (commonType == null) {
        return null;
    }
    // Common return type found: all factory methods return same type. For a non-parameterized
    // unique candidate, cache the full type declaration context of the target factory method.
    cachedReturnType = (uniqueCandidate != null ? ResolvableType.forMethodReturnType(uniqueCandidate)
            : ResolvableType.forClass(commonType));
    mbd.factoryMethodReturnType = cachedReturnType;
    return cachedReturnType.resolve();
}