diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index feeb34d188a..04a68b4376b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -752,7 +752,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac } Class returnType = AutowireUtils.resolveReturnTypeForFactoryMethod( factoryMethod, args, getBeanClassLoader()); - uniqueCandidate = (commonType == null ? factoryMethod : null); + uniqueCandidate = (commonType == null && returnType == factoryMethod.getReturnType() ? + factoryMethod : null); commonType = ClassUtils.determineCommonAncestor(returnType, commonType); if (commonType == null) { // Ambiguous return types found: return null to indicate "not determinable". @@ -776,12 +777,15 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac } } - if (commonType != null) { - // Clear return type found: all factory methods return same type. - mbd.factoryMethodReturnType = (uniqueCandidate != null ? - ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType)); + if (commonType == null) { + return null; } - return commonType; + // Common return type found: all factory methods return same type. For a non-parameterized + // unique candidate, cache the full type declaration context of the target factory method. + cachedReturnType = (uniqueCandidate != null ? + ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType)); + mbd.factoryMethodReturnType = cachedReturnType; + return cachedReturnType.resolve(); } /** diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java index efef1b0ac68..4a0fcec0716 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -40,6 +40,7 @@ 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.OverridingClassLoader; import org.springframework.core.ResolvableType; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.UrlResource; @@ -672,6 +673,8 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerBeanDefinition("mock", rbd); + assertEquals(Runnable.class, bf.getType("mock")); + assertEquals(Runnable.class, bf.getType("mock")); Map beans = bf.getBeansOfType(Runnable.class); assertEquals(1, beans.size()); } @@ -700,6 +703,10 @@ public class BeanFactoryGenericsTests { rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class); bf.registerBeanDefinition("mock", rbd); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertEquals(Runnable.class, bf.getType("mock")); + assertEquals(Runnable.class, bf.getType("mock")); Map beans = bf.getBeansOfType(Runnable.class); assertEquals(1, beans.size()); } @@ -717,6 +724,10 @@ public class BeanFactoryGenericsTests { rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class.getName()); bf.registerBeanDefinition("mock", rbd); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertEquals(Runnable.class, bf.getType("mock")); + assertEquals(Runnable.class, bf.getType("mock")); Map beans = bf.getBeansOfType(Runnable.class); assertEquals(1, beans.size()); } @@ -732,6 +743,10 @@ public class BeanFactoryGenericsTests { rbd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Runnable.class.getName())); bf.registerBeanDefinition("mock", rbd); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertEquals(Runnable.class, bf.getType("mock")); + assertEquals(Runnable.class, bf.getType("mock")); Map beans = bf.getBeansOfType(Runnable.class); assertEquals(1, beans.size()); } @@ -749,6 +764,10 @@ public class BeanFactoryGenericsTests { rbd.getConstructorArgumentValues().addGenericArgumentValue("x"); bf.registerBeanDefinition("mock", rbd); + assertFalse(bf.isTypeMatch("mock", Runnable.class)); + assertFalse(bf.isTypeMatch("mock", Runnable.class)); + assertNull(bf.getType("mock")); + assertNull(bf.getType("mock")); Map beans = bf.getBeansOfType(Runnable.class); assertEquals(0, beans.size()); } @@ -766,6 +785,32 @@ public class BeanFactoryGenericsTests { rbd.getConstructorArgumentValues().addIndexedArgumentValue(0, Runnable.class); bf.registerBeanDefinition("mock", rbd); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertEquals(Runnable.class, bf.getType("mock")); + assertEquals(Runnable.class, bf.getType("mock")); + Map beans = bf.getBeansOfType(Runnable.class); + assertEquals(1, beans.size()); + } + + @Test // SPR-16720 + public void parameterizedInstanceFactoryMethodWithTempClassLoader() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setTempClassLoader(new OverridingClassLoader(getClass().getClassLoader())); + + 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); + + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertTrue(bf.isTypeMatch("mock", Runnable.class)); + assertEquals(Runnable.class, bf.getType("mock")); + assertEquals(Runnable.class, bf.getType("mock")); Map beans = bf.getBeansOfType(Runnable.class); assertEquals(1, beans.size()); }