Factory method type resolution works with indexed and named arguments as well
Issue: SPR-11019
(cherry picked from commit 109faac
)
This commit is contained in:
parent
ce001c23f7
commit
42568afb37
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -146,7 +146,7 @@ public class ConstructorArgumentValues {
|
|||
* untyped values only)
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ public class ConstructorArgumentValues {
|
|||
* unnamed values only)
|
||||
* @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");
|
||||
ValueHolder valueHolder = this.indexedArgumentValues.get(index);
|
||||
if (valueHolder != null &&
|
||||
|
@ -247,7 +247,7 @@ public class ConstructorArgumentValues {
|
|||
* @param requiredType the type to match
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -257,7 +257,7 @@ public class ConstructorArgumentValues {
|
|||
* @param requiredName the name to match
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ public class ConstructorArgumentValues {
|
|||
* in the current resolution process and should therefore not be returned again
|
||||
* @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) {
|
||||
if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) {
|
||||
continue;
|
||||
|
@ -309,10 +309,10 @@ public class ConstructorArgumentValues {
|
|||
* Look for an argument value that either corresponds to the given index
|
||||
* in the constructor argument list or generically matches by type.
|
||||
* @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
|
||||
*/
|
||||
public ValueHolder getArgumentValue(int index, Class requiredType) {
|
||||
public ValueHolder getArgumentValue(int index, Class<?> requiredType) {
|
||||
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
|
||||
* in the constructor argument list or generically matches by type.
|
||||
* @param index the index in the constructor argument list
|
||||
* @param requiredType the type to match
|
||||
* @param requiredName the name to match
|
||||
* @param requiredType the parameter type to match
|
||||
* @param requiredName the parameter name to match
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
@ -332,15 +332,17 @@ public class ConstructorArgumentValues {
|
|||
* Look for an argument value that either corresponds to the given index
|
||||
* in the constructor argument list or generically matches by type.
|
||||
* @param index the index in the constructor argument list
|
||||
* @param requiredType the type to match (can be {@code null} to find
|
||||
* an untyped argument value)
|
||||
* @param requiredType the parameter type to match (can be {@code null}
|
||||
* 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
|
||||
* been used in the current resolution process and should therefore not
|
||||
* be returned again (allowing to return the next generic argument match
|
||||
* in case of multiple generic argument values of the same type)
|
||||
* @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");
|
||||
ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName);
|
||||
if (valueHolder == null) {
|
||||
|
|
|
@ -63,7 +63,7 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
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.InstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
|
@ -632,12 +632,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
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.
|
||||
// Can't clearly figure out exact method due to type converting / autowiring!
|
||||
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
|
||||
|
@ -647,6 +641,27 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
|
||||
factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
|
||||
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(
|
||||
factoryMethod, args, getBeanClassLoader());
|
||||
if (returnType != null) {
|
||||
|
|
|
@ -16,13 +16,9 @@
|
|||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
@ -37,6 +33,7 @@ import java.util.Set;
|
|||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.PropertyEditorRegistrar;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
|
@ -46,12 +43,12 @@ import org.springframework.core.io.ClassPathResource;
|
|||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.tests.Assume;
|
||||
import org.springframework.tests.TestGroup;
|
||||
|
||||
import org.springframework.tests.sample.beans.GenericBean;
|
||||
import org.springframework.tests.sample.beans.GenericIntegerBean;
|
||||
import org.springframework.tests.sample.beans.GenericSetOfIntegerBean;
|
||||
import org.springframework.tests.sample.beans.TestBean;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
|
@ -115,7 +112,7 @@ public class BeanFactoryGenericsTests {
|
|||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class);
|
||||
|
||||
List input = new ArrayList();
|
||||
List<Integer> input = new ArrayList<Integer>();
|
||||
input.add(1);
|
||||
rbd.getPropertyValues().add("testBeanList", input);
|
||||
|
||||
|
@ -655,18 +652,22 @@ public class BeanFactoryGenericsTests {
|
|||
}
|
||||
|
||||
/**
|
||||
* Tests support for parameterized {@code factory-method} declarations such
|
||||
* as Mockito {@code mock()} method which has the following signature.
|
||||
*
|
||||
* <pre>{@code
|
||||
* Tests support for parameterized static {@code factory-method} declarations such as
|
||||
* Mockito's {@code mock()} method which has the following signature.
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* public static <T> T mock(Class<T> classToMock)
|
||||
* }</pre>
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* See SPR-9493
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
@Test
|
||||
public void parameterizedFactoryMethod() {
|
||||
public void parameterizedStaticFactoryMethod() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(Mockito.class);
|
||||
rbd.setFactoryMethodName("mock");
|
||||
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
|
||||
|
@ -678,6 +679,71 @@ public class BeanFactoryGenericsTests {
|
|||
assertEquals(1, beans.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests support for parameterized instance {@code factory-method} declarations such
|
||||
* as EasyMock's {@code IMocksControl.createMock()} method which has the following
|
||||
* signature.
|
||||
* <pre>
|
||||
* {@code
|
||||
* public <T> T createMock(Class<T> toMock)
|
||||
* }
|
||||
* </pre>
|
||||
* <p>See SPR-10411
|
||||
*/
|
||||
@Test
|
||||
public void parameterizedInstanceFactoryMethod() {
|
||||
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);
|
||||
|
||||
bf.registerBeanDefinition("mock", rbd);
|
||||
|
||||
Map<String, Runnable> beans = bf.getBeansOfType(Runnable.class);
|
||||
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")
|
||||
public static class NamedUrlList extends LinkedList<URL> {
|
||||
|
@ -722,4 +788,25 @@ public class BeanFactoryGenericsTests {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pseudo-implementation of EasyMock's {@code MocksControl} class.
|
||||
*/
|
||||
public static class MocksControl {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T createMock(Class<T> toMock) {
|
||||
|
||||
return (T) Proxy.newProxyInstance(
|
||||
BeanFactoryGenericsTests.class.getClassLoader(),
|
||||
new Class[] { toMock }, new InvocationHandler() {
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
throw new UnsupportedOperationException("mocked!");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue