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:
Juergen Hoeller 2013-10-27 21:26:04 +01:00
parent 671fad3cb5
commit 960ba379ca
3 changed files with 76 additions and 12 deletions

View File

@ -32,6 +32,7 @@ import java.util.Comparator;
import java.util.Set;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@ -222,19 +223,28 @@ abstract class AutowireUtils {
if (arg instanceof Class) {
return (Class<?>) arg;
}
else if (arg instanceof String) {
try {
return classLoader.loadClass((String) arg);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Could not resolve specified class name argument [" + arg + "]", ex);
}
}
else {
// Consider adding logic to determine the class of the typeArg, if possible.
// For now, just fall back...
return method.getReturnType();
String className = null;
if (arg instanceof String) {
className = (String) arg;
}
else if (arg instanceof TypedStringValue) {
className = ((TypedStringValue) arg).getValue();
}
if (className != null) {
try {
return classLoader.loadClass(className);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Could not resolve specified class name argument [" + arg + "]", ex);
}
}
else {
// Consider adding logic to determine the class of the typeArg, if possible.
// For now, just fall back...
return method.getReturnType();
}
}
}
}

View File

@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
@ -35,6 +36,7 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
@ -1355,6 +1357,42 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(repo, bean.stringRepositoryMap.get("repo"));
}
@Test
public void testGenericsBasedFieldInjectionWithSimpleMatchAndMockito() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSimpleMatch.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
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(Repository.class.getName()));
bf.registerBeanDefinition("repo", rbd);
RepositoryFieldInjectionBeanWithSimpleMatch bean = (RepositoryFieldInjectionBeanWithSimpleMatch) bf.getBean("annotatedBean");
Repository repo = bf.getBean("repo", Repository.class);
assertSame(repo, bean.repository);
assertSame(repo, bean.stringRepository);
assertSame(1, bean.repositoryArray.length);
assertSame(1, bean.stringRepositoryArray.length);
assertSame(repo, bean.repositoryArray[0]);
assertSame(repo, bean.stringRepositoryArray[0]);
assertSame(1, bean.repositoryList.size());
assertSame(1, bean.stringRepositoryList.size());
assertSame(repo, bean.repositoryList.get(0));
assertSame(repo, bean.stringRepositoryList.get(0));
assertSame(1, bean.repositoryMap.size());
assertSame(1, bean.stringRepositoryMap.size());
assertSame(repo, bean.repositoryMap.get("repo"));
assertSame(repo, bean.stringRepositoryMap.get("repo"));
}
@Test
public void testGenericsBasedMethodInjection() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();

View File

@ -37,6 +37,7 @@ import org.mockito.Mockito;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.core.io.ClassPathResource;
@ -719,6 +720,21 @@ public class BeanFactoryGenericsTests {
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
public void parameterizedInstanceFactoryMethodWithIndexedArgument() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();