ResolvableType-based matching respects generic factory method return type
Includes consistent use of ResolvableType.resolve() wherever applicable. Issue: SPR-15011
This commit is contained in:
@@ -109,7 +109,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
|
||||
* that failed.
|
||||
*/
|
||||
public Class<?> getBeanType() {
|
||||
return (this.resolvableType != null ? this.resolvableType.getRawClass() : null);
|
||||
return (this.resolvableType != null ? this.resolvableType.resolve() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,6 +74,7 @@ import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -672,9 +673,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
* @see #createBean
|
||||
*/
|
||||
protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
|
||||
Class<?> preResolved = mbd.resolvedFactoryMethodReturnType;
|
||||
if (preResolved != null) {
|
||||
return preResolved;
|
||||
ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
|
||||
if (cachedReturnType != null) {
|
||||
return cachedReturnType.resolve();
|
||||
}
|
||||
|
||||
Class<?> factoryClass;
|
||||
@@ -698,11 +699,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
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;
|
||||
boolean cache = false;
|
||||
Method uniqueCandidate = null;
|
||||
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
|
||||
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass);
|
||||
for (Method factoryMethod : candidates) {
|
||||
@@ -736,8 +738,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
|
||||
factoryMethod, args, getBeanClassLoader());
|
||||
if (returnType != null) {
|
||||
cache = true;
|
||||
uniqueCandidate = (commonType == null ? factoryMethod : null);
|
||||
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
|
||||
if (commonType == null) {
|
||||
// Ambiguous return types found: return null to indicate "not determinable".
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
@@ -747,22 +753,22 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
}
|
||||
}
|
||||
else {
|
||||
uniqueCandidate = (commonType == null ? factoryMethod : null);
|
||||
commonType = ClassUtils.determineCommonAncestor(factoryMethod.getReturnType(), commonType);
|
||||
if (commonType == null) {
|
||||
// Ambiguous return types found: return null to indicate "not determinable".
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (commonType != null) {
|
||||
// Clear return type found: all factory methods return same type.
|
||||
if (cache) {
|
||||
mbd.resolvedFactoryMethodReturnType = commonType;
|
||||
}
|
||||
return commonType;
|
||||
}
|
||||
else {
|
||||
// Ambiguous return types found: return null to indicate "not determinable".
|
||||
return null;
|
||||
mbd.factoryMethodReturnType = (uniqueCandidate != null ?
|
||||
ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
|
||||
}
|
||||
return commonType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -513,7 +513,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
// Retrieve corresponding bean definition.
|
||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||
|
||||
Class<?> classToMatch = typeToMatch.getRawClass();
|
||||
Class<?> classToMatch = typeToMatch.resolve();
|
||||
if (classToMatch == null) {
|
||||
classToMatch = FactoryBean.class;
|
||||
}
|
||||
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
|
||||
new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});
|
||||
|
||||
@@ -553,6 +556,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
}
|
||||
}
|
||||
|
||||
ResolvableType resolvableType = mbd.targetType;
|
||||
if (resolvableType == null) {
|
||||
resolvableType = mbd.factoryMethodReturnType;
|
||||
}
|
||||
if (resolvableType != null && resolvableType.resolve() == beanType) {
|
||||
return typeToMatch.isAssignableFrom(resolvableType);
|
||||
}
|
||||
return typeToMatch.isAssignableFrom(beanType);
|
||||
}
|
||||
}
|
||||
@@ -1443,6 +1453,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* @return the type of the bean, or {@code null} if not predictable
|
||||
*/
|
||||
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
|
||||
Class<?> targetType = mbd.getTargetType();
|
||||
if (targetType != null) {
|
||||
return targetType;
|
||||
}
|
||||
if (mbd.getFactoryMethodName() != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -162,8 +162,8 @@ abstract class AutowireUtils {
|
||||
* Determine the target type for the generic return type of the given
|
||||
* <em>generic factory method</em>, where formal type variables are declared
|
||||
* on the given method itself.
|
||||
* <p>For example, given a factory method with the following signature,
|
||||
* if {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
|
||||
* <p>For example, given a factory method with the following signature, if
|
||||
* {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
|
||||
* method for {@code creatProxy()} and an {@code Object[]} array containing
|
||||
* {@code MyService.class}, {@code resolveReturnTypeForFactoryMethod()} will
|
||||
* infer that the target return type is {@code MyService}.
|
||||
@@ -184,9 +184,9 @@ abstract class AutowireUtils {
|
||||
* @param method the method to introspect (never {@code null})
|
||||
* @param args the arguments that will be supplied to the method when it is
|
||||
* invoked (never {@code null})
|
||||
* @param classLoader the ClassLoader to resolve class names against, if necessary
|
||||
* (never {@code null})
|
||||
* @return the resolved target return type, the standard return type, or {@code null}
|
||||
* @param classLoader the ClassLoader to resolve class names against,
|
||||
* if necessary (never {@code null})
|
||||
* @return the resolved target return type or the standard method return type
|
||||
* @since 3.2.5
|
||||
*/
|
||||
public static Class<?> resolveReturnTypeForFactoryMethod(Method method, Object[] args, ClassLoader classLoader) {
|
||||
|
||||
@@ -147,22 +147,23 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
|
||||
protected ResolvableType getReturnTypeForFactoryMethod(RootBeanDefinition rbd, DependencyDescriptor descriptor) {
|
||||
// Should typically be set for any kind of factory method, since the BeanFactory
|
||||
// pre-resolves them before reaching out to the AutowireCandidateResolver...
|
||||
Class<?> preResolved = rbd.resolvedFactoryMethodReturnType;
|
||||
if (preResolved != null) {
|
||||
return ResolvableType.forClass(preResolved);
|
||||
}
|
||||
else {
|
||||
Method resolvedFactoryMethod = rbd.getResolvedFactoryMethod();
|
||||
if (resolvedFactoryMethod != null) {
|
||||
if (descriptor.getDependencyType().isAssignableFrom(resolvedFactoryMethod.getReturnType())) {
|
||||
// Only use factory method metadata if the return type is actually expressive enough
|
||||
// for our dependency. Otherwise, the returned instance type may have matched instead
|
||||
// in case of a singleton instance having been registered with the container already.
|
||||
return ResolvableType.forMethodReturnType(resolvedFactoryMethod);
|
||||
}
|
||||
ResolvableType returnType = rbd.factoryMethodReturnType;
|
||||
if (returnType == null) {
|
||||
Method factoryMethod = rbd.getResolvedFactoryMethod();
|
||||
if (factoryMethod != null) {
|
||||
returnType = ResolvableType.forMethodReturnType(factoryMethod);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (returnType != null) {
|
||||
Class<?> resolvedClass = returnType.resolve();
|
||||
if (resolvedClass != null && descriptor.getDependencyType().isAssignableFrom(resolvedClass)) {
|
||||
// Only use factory method metadata if the return type is actually expressive enough
|
||||
// for our dependency. Otherwise, the returned instance type may have matched instead
|
||||
// in case of a singleton instance having been registered with the container already.
|
||||
return returnType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
volatile Class<?> resolvedTargetType;
|
||||
|
||||
/** Package-visible field for caching the return type of a generically typed factory method */
|
||||
volatile Class<?> resolvedFactoryMethodReturnType;
|
||||
volatile ResolvableType factoryMethodReturnType;
|
||||
|
||||
/** Common lock for the four constructor fields below */
|
||||
final Object constructorArgumentLock = new Object();
|
||||
|
||||
@@ -248,7 +248,13 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
|
||||
|
||||
@Override
|
||||
public String[] getBeanNamesForType(ResolvableType type) {
|
||||
boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type.getRawClass()));
|
||||
boolean isFactoryType = false;
|
||||
if (type != null) {
|
||||
Class<?> resolved = type.resolve();
|
||||
if (resolved != null && FactoryBean.class.isAssignableFrom(resolved)) {
|
||||
isFactoryType = true;
|
||||
}
|
||||
}
|
||||
List<String> matches = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : this.beans.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
|
||||
Reference in New Issue
Block a user