Fixed resolveReturnTypeForFactoryMethod to unwrap TypedStringValue
XML-defined arguments values are initially turned into TypedStringValue wrappers. If we encounter an unresolved argument, we need to unwrap such a TypedStringValue and then try to treat its content as a class name. Issue: SPR-11034
This commit is contained in:
parent
3eaec64ee8
commit
44f79f95a7
|
@ -31,7 +31,9 @@ import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanMetadataElement;
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
import org.springframework.beans.factory.config.TypedStringValue;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
@ -192,8 +194,8 @@ abstract class AutowireUtils {
|
||||||
|
|
||||||
TypeVariable<Method>[] declaredTypeVariables = method.getTypeParameters();
|
TypeVariable<Method>[] declaredTypeVariables = method.getTypeParameters();
|
||||||
Type genericReturnType = method.getGenericReturnType();
|
Type genericReturnType = method.getGenericReturnType();
|
||||||
Type[] methodArgumentTypes = method.getGenericParameterTypes();
|
Type[] methodParameterTypes = method.getGenericParameterTypes();
|
||||||
Assert.isTrue(args.length == methodArgumentTypes.length, "Argument array does not match parameter count");
|
Assert.isTrue(args.length == methodParameterTypes.length, "Argument array does not match parameter count");
|
||||||
|
|
||||||
// Ensure that the type variable (e.g., T) is declared directly on the method
|
// Ensure that the type variable (e.g., T) is declared directly on the method
|
||||||
// itself (e.g., via <T>), not on the enclosing class or interface.
|
// itself (e.g., via <T>), not on the enclosing class or interface.
|
||||||
|
@ -206,30 +208,57 @@ abstract class AutowireUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locallyDeclaredTypeVariableMatchesReturnType) {
|
if (locallyDeclaredTypeVariableMatchesReturnType) {
|
||||||
for (int i = 0; i < methodArgumentTypes.length; i++) {
|
for (int i = 0; i < methodParameterTypes.length; i++) {
|
||||||
Type currentMethodArgumentType = methodArgumentTypes[i];
|
Type methodParameterType = methodParameterTypes[i];
|
||||||
if (currentMethodArgumentType.equals(genericReturnType)) {
|
Object arg = args[i];
|
||||||
return args[i].getClass();
|
if (methodParameterType.equals(genericReturnType)) {
|
||||||
|
if (arg instanceof TypedStringValue) {
|
||||||
|
TypedStringValue typedValue = ((TypedStringValue) arg);
|
||||||
|
if (typedValue.hasTargetType()) {
|
||||||
|
return typedValue.getTargetType();
|
||||||
}
|
}
|
||||||
if (currentMethodArgumentType instanceof ParameterizedType) {
|
try {
|
||||||
ParameterizedType parameterizedType = (ParameterizedType) currentMethodArgumentType;
|
return typedValue.resolveTargetType(classLoader);
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex) {
|
||||||
|
throw new IllegalStateException("Failed to resolve typed value", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only consider argument type if it is a simple value...
|
||||||
|
if (arg != null && !(arg instanceof BeanMetadataElement)) {
|
||||||
|
return arg.getClass();
|
||||||
|
}
|
||||||
|
return method.getReturnType();
|
||||||
|
}
|
||||||
|
else if (methodParameterType instanceof ParameterizedType) {
|
||||||
|
ParameterizedType parameterizedType = (ParameterizedType) methodParameterType;
|
||||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||||
for (Type typeArg : actualTypeArguments) {
|
for (Type typeArg : actualTypeArguments) {
|
||||||
if (typeArg.equals(genericReturnType)) {
|
if (typeArg.equals(genericReturnType)) {
|
||||||
Object arg = args[i];
|
|
||||||
if (arg instanceof Class) {
|
if (arg instanceof Class) {
|
||||||
return (Class<?>) arg;
|
return (Class<?>) arg;
|
||||||
}
|
}
|
||||||
else if (arg instanceof String) {
|
else {
|
||||||
|
String className = null;
|
||||||
|
if (arg instanceof String) {
|
||||||
|
className = (String) arg;
|
||||||
|
}
|
||||||
|
else if (arg instanceof TypedStringValue) {
|
||||||
|
TypedStringValue typedValue = ((TypedStringValue) arg);
|
||||||
|
String targetTypeName = typedValue.getTargetTypeName();
|
||||||
|
if (targetTypeName == null || Class.class.getName().equals(targetTypeName)) {
|
||||||
|
className = typedValue.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (className != null) {
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass((String) arg);
|
return classLoader.loadClass(className);
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException ex) {
|
catch (ClassNotFoundException ex) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Could not resolve specified class name argument [" + arg + "]", ex);
|
"Could not resolve specified class name argument [" + arg + "]", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Consider adding logic to determine the class of the typeArg, if possible.
|
// Consider adding logic to determine the class of the typeArg, if possible.
|
||||||
// For now, just fall back...
|
// For now, just fall back...
|
||||||
return method.getReturnType();
|
return method.getReturnType();
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.mockito.Mockito;
|
||||||
import org.springframework.beans.PropertyEditorRegistrar;
|
import org.springframework.beans.PropertyEditorRegistrar;
|
||||||
import org.springframework.beans.PropertyEditorRegistry;
|
import org.springframework.beans.PropertyEditorRegistry;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
|
import org.springframework.beans.factory.config.TypedStringValue;
|
||||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||||
import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
@ -720,6 +721,21 @@ public class BeanFactoryGenericsTests {
|
||||||
assertEquals(1, beans.size());
|
assertEquals(1, beans.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parameterizedInstanceFactoryMethodWithWrappedClassName() {
|
||||||
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
|
||||||
|
RootBeanDefinition rbd = new RootBeanDefinition();
|
||||||
|
rbd.setBeanClassName(Mockito.class.getName());
|
||||||
|
rbd.setFactoryMethodName("mock");
|
||||||
|
// TypedStringValue used to be equivalent to an XML-defined argument String
|
||||||
|
rbd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Runnable.class.getName()));
|
||||||
|
bf.registerBeanDefinition("mock", rbd);
|
||||||
|
|
||||||
|
Map<String, Runnable> beans = bf.getBeansOfType(Runnable.class);
|
||||||
|
assertEquals(1, beans.size());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parameterizedInstanceFactoryMethodWithIndexedArgument() {
|
public void parameterizedInstanceFactoryMethodWithIndexedArgument() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
|
Loading…
Reference in New Issue