Factory method type resolution works with indexed and named arguments as well

Issue: SPR-11019
This commit is contained in:
Juergen Hoeller 2013-10-26 14:05:37 +02:00
parent 4675bc4e0c
commit 109faaced8
3 changed files with 77 additions and 27 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -146,7 +146,7 @@ public class ConstructorArgumentValues {
* untyped values only) * untyped values only)
* @return the ValueHolder for the argument, or {@code null} if none set * @return the ValueHolder for the argument, or {@code null} if none set
*/ */
public ValueHolder getIndexedArgumentValue(int index, Class requiredType) { public ValueHolder getIndexedArgumentValue(int index, Class<?> requiredType) {
return getIndexedArgumentValue(index, requiredType, null); return getIndexedArgumentValue(index, requiredType, null);
} }
@ -159,7 +159,7 @@ public class ConstructorArgumentValues {
* unnamed values only) * unnamed values only)
* @return the ValueHolder for the argument, or {@code null} if none set * @return the ValueHolder for the argument, or {@code null} if none set
*/ */
public ValueHolder getIndexedArgumentValue(int index, Class requiredType, String requiredName) { public ValueHolder getIndexedArgumentValue(int index, Class<?> requiredType, String requiredName) {
Assert.isTrue(index >= 0, "Index must not be negative"); Assert.isTrue(index >= 0, "Index must not be negative");
ValueHolder valueHolder = this.indexedArgumentValues.get(index); ValueHolder valueHolder = this.indexedArgumentValues.get(index);
if (valueHolder != null && if (valueHolder != null &&
@ -247,7 +247,7 @@ public class ConstructorArgumentValues {
* @param requiredType the type to match * @param requiredType the type to match
* @return the ValueHolder for the argument, or {@code null} if none set * @return the ValueHolder for the argument, or {@code null} if none set
*/ */
public ValueHolder getGenericArgumentValue(Class requiredType) { public ValueHolder getGenericArgumentValue(Class<?> requiredType) {
return getGenericArgumentValue(requiredType, null, null); return getGenericArgumentValue(requiredType, null, null);
} }
@ -257,7 +257,7 @@ public class ConstructorArgumentValues {
* @param requiredName the name to match * @param requiredName the name to match
* @return the ValueHolder for the argument, or {@code null} if none set * @return the ValueHolder for the argument, or {@code null} if none set
*/ */
public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName) { public ValueHolder getGenericArgumentValue(Class<?> requiredType, String requiredName) {
return getGenericArgumentValue(requiredType, requiredName, null); return getGenericArgumentValue(requiredType, requiredName, null);
} }
@ -273,7 +273,7 @@ public class ConstructorArgumentValues {
* in the current resolution process and should therefore not be returned again * in the current resolution process and should therefore not be returned again
* @return the ValueHolder for the argument, or {@code null} if none found * @return the ValueHolder for the argument, or {@code null} if none found
*/ */
public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName, Set<ValueHolder> usedValueHolders) { public ValueHolder getGenericArgumentValue(Class<?> requiredType, String requiredName, Set<ValueHolder> usedValueHolders) {
for (ValueHolder valueHolder : this.genericArgumentValues) { for (ValueHolder valueHolder : this.genericArgumentValues) {
if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) { if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) {
continue; continue;
@ -309,10 +309,10 @@ public class ConstructorArgumentValues {
* Look for an argument value that either corresponds to the given index * Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type. * in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list * @param index the index in the constructor argument list
* @param requiredType the type to match * @param requiredType the parameter type to match
* @return the ValueHolder for the argument, or {@code null} if none set * @return the ValueHolder for the argument, or {@code null} if none set
*/ */
public ValueHolder getArgumentValue(int index, Class requiredType) { public ValueHolder getArgumentValue(int index, Class<?> requiredType) {
return getArgumentValue(index, requiredType, null, null); return getArgumentValue(index, requiredType, null, null);
} }
@ -320,11 +320,11 @@ public class ConstructorArgumentValues {
* Look for an argument value that either corresponds to the given index * Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type. * in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list * @param index the index in the constructor argument list
* @param requiredType the type to match * @param requiredType the parameter type to match
* @param requiredName the name to match * @param requiredName the parameter name to match
* @return the ValueHolder for the argument, or {@code null} if none set * @return the ValueHolder for the argument, or {@code null} if none set
*/ */
public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName) { public ValueHolder getArgumentValue(int index, Class<?> requiredType, String requiredName) {
return getArgumentValue(index, requiredType, requiredName, null); return getArgumentValue(index, requiredType, requiredName, null);
} }
@ -332,15 +332,17 @@ public class ConstructorArgumentValues {
* Look for an argument value that either corresponds to the given index * Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type. * in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list * @param index the index in the constructor argument list
* @param requiredType the type to match (can be {@code null} to find * @param requiredType the parameter type to match (can be {@code null}
* an untyped argument value) * to find an untyped argument value)
* @param requiredName the parameter name to match (can be {@code null}
* to find an unnamed argument value)
* @param usedValueHolders a Set of ValueHolder objects that have already * @param usedValueHolders a Set of ValueHolder objects that have already
* been used in the current resolution process and should therefore not * been used in the current resolution process and should therefore not
* be returned again (allowing to return the next generic argument match * be returned again (allowing to return the next generic argument match
* in case of multiple generic argument values of the same type) * in case of multiple generic argument values of the same type)
* @return the ValueHolder for the argument, or {@code null} if none set * @return the ValueHolder for the argument, or {@code null} if none set
*/ */
public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName, Set<ValueHolder> usedValueHolders) { public ValueHolder getArgumentValue(int index, Class<?> requiredType, String requiredName, Set<ValueHolder> usedValueHolders) {
Assert.isTrue(index >= 0, "Index must not be negative"); Assert.isTrue(index >= 0, "Index must not be negative");
ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName); ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName);
if (valueHolder == null) { if (valueHolder == null) {

View File

@ -63,7 +63,7 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
@ -650,12 +650,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
return null; return null;
} }
List<ValueHolder> argumentValues = mbd.getConstructorArgumentValues().getGenericArgumentValues();
Object[] args = new Object[argumentValues.size()];
for (int i = 0; i < args.length; i++) {
args[i] = argumentValues.get(i).getValue();
}
// If all factory methods have the same return type, return that type. // If all factory methods have the same return type, return that type.
// Can't clearly figure out exact method due to type converting / autowiring! // Can't clearly figure out exact method due to type converting / autowiring!
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount(); int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
@ -665,6 +659,27 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic && if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
factoryMethod.getName().equals(mbd.getFactoryMethodName()) && factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
factoryMethod.getParameterTypes().length >= minNrOfArgs) { factoryMethod.getParameterTypes().length >= minNrOfArgs) {
Class<?>[] paramTypes = factoryMethod.getParameterTypes();
String[] paramNames = null;
ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(factoryMethod);
}
ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders =
new HashSet<ConstructorArgumentValues.ValueHolder>(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( Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
factoryMethod, args, getBeanClassLoader()); factoryMethod, args, getBeanClassLoader());
if (returnType != null) { if (returnType != null) {

View File

@ -33,6 +33,7 @@ import java.util.Set;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; 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;
@ -682,17 +683,12 @@ public class BeanFactoryGenericsTests {
* Tests support for parameterized instance {@code factory-method} declarations such * Tests support for parameterized instance {@code factory-method} declarations such
* as EasyMock's {@code IMocksControl.createMock()} method which has the following * as EasyMock's {@code IMocksControl.createMock()} method which has the following
* signature. * signature.
*
* <pre> * <pre>
* {@code * {@code
* public <T> T createMock(Class<T> toMock) * public <T> T createMock(Class<T> toMock)
* } * }
* </pre> * </pre>
* * <p>See SPR-10411
* <p>
* See SPR-10411
*
* @since 4.0
*/ */
@Test @Test
public void parameterizedInstanceFactoryMethod() { public void parameterizedInstanceFactoryMethod() {
@ -712,6 +708,43 @@ public class BeanFactoryGenericsTests {
assertEquals(1, beans.size()); assertEquals(1, beans.size());
} }
@Test
public void parameterizedInstanceFactoryMethodWithNonResolvedClassName() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
bf.registerBeanDefinition("mocksControl", rbd);
rbd = new RootBeanDefinition();
rbd.setFactoryBeanName("mocksControl");
rbd.setFactoryMethodName("createMock");
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class.getName());
bf.registerBeanDefinition("mock", rbd);
Map<String, Runnable> beans = bf.getBeansOfType(Runnable.class);
assertEquals(1, beans.size());
}
@Test
public void parameterizedInstanceFactoryMethodWithIndexedArgument() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
bf.registerBeanDefinition("mocksControl", rbd);
rbd = new RootBeanDefinition();
rbd.setFactoryBeanName("mocksControl");
rbd.setFactoryMethodName("createMock");
rbd.getConstructorArgumentValues().addIndexedArgumentValue(0, Runnable.class);
bf.registerBeanDefinition("mock", rbd);
Map<String, Runnable> beans = bf.getBeansOfType(Runnable.class);
assertEquals(1, beans.size());
}
@SuppressWarnings("serial") @SuppressWarnings("serial")
public static class NamedUrlList extends LinkedList<URL> { public static class NamedUrlList extends LinkedList<URL> {
} }