From c6a7732a30b169f342c4200aedc88e01c71c43d4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 12 Apr 2018 15:14:41 +0200 Subject: [PATCH] Consistent getTypeForFactoryMethod result for parameterized method Issue: SPR-16720 (cherry picked from commit 6184c4e) --- .../AbstractAutowireCapableBeanFactory.java | 16 ++- .../support/BeanFactoryGenericsTests.java | 107 +++++++++++++----- 2 files changed, 86 insertions(+), 37 deletions(-) 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 00adea84c9c..de743f4d32f 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 @@ -727,7 +727,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac Class returnType = AutowireUtils.resolveReturnTypeForFactoryMethod( factoryMethod, args, getBeanClassLoader()); if (returnType != null) { - 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". @@ -752,12 +753,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 ed1b506b7a7..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-2015 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; @@ -65,7 +66,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Set input = new HashSet(); + Set input = new HashSet<>(); input.add("4"); input.add("5"); rbd.getPropertyValues().add("integerSet", input); @@ -82,7 +83,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - List input = new ArrayList(); + List input = new ArrayList<>(); input.add("http://localhost:8080"); input.add("http://localhost:9090"); rbd.getPropertyValues().add("resourceList", input); @@ -114,7 +115,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class); - List input = new ArrayList(); + List input = new ArrayList<>(); input.add(1); rbd.getPropertyValues().add("testBeanList", input); @@ -146,7 +147,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("4", "5"); input.put("6", "7"); rbd.getPropertyValues().add("shortMap", input); @@ -178,7 +179,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Set input = new HashSet(); + Set input = new HashSet<>(); input.add("4"); input.add("5"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -222,10 +223,10 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Set input = new HashSet(); + Set input = new HashSet<>(); input.add("4"); input.add("5"); - List input2 = new ArrayList(); + List input2 = new ArrayList<>(); input2.add("http://localhost:8080"); input2.add("http://localhost:9090"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -279,10 +280,10 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Set input = new HashSet(); + Set input = new HashSet<>(); input.add("4"); input.add("5"); - Map input2 = new HashMap(); + Map input2 = new HashMap<>(); input2.put("4", "5"); input2.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -302,7 +303,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("4", "5"); input.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -321,10 +322,10 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("1", "0"); input.put("2", "3"); - Map input2 = new HashMap(); + Map input2 = new HashMap<>(); input2.put("4", "5"); input2.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -347,7 +348,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("1", "0"); input.put("2", "3"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -370,7 +371,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put(new Short((short) 1), new Integer(0)); input.put(new Short((short) 2), new Integer(3)); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -390,7 +391,7 @@ public class BeanFactoryGenericsTests { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("4", "5"); input.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -413,11 +414,11 @@ public class BeanFactoryGenericsTests { }); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map> input = new HashMap>(); - HashSet value1 = new HashSet(); + Map> input = new HashMap<>(); + HashSet value1 = new HashSet<>(); value1.add(new Integer(1)); input.put("1", value1); - ArrayList value2 = new ArrayList(); + ArrayList value2 = new ArrayList<>(); value2.add(Boolean.TRUE); input.put("2", value2); rbd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE); @@ -437,7 +438,7 @@ public class BeanFactoryGenericsTests { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setFactoryMethodName("createInstance"); - Set input = new HashSet(); + Set input = new HashSet<>(); input.add("4"); input.add("5"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -455,10 +456,10 @@ public class BeanFactoryGenericsTests { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setFactoryMethodName("createInstance"); - Set input = new HashSet(); + Set input = new HashSet<>(); input.add("4"); input.add("5"); - List input2 = new ArrayList(); + List input2 = new ArrayList<>(); input2.add("http://localhost:8080"); input2.add("http://localhost:9090"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -479,10 +480,10 @@ public class BeanFactoryGenericsTests { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setFactoryMethodName("createInstance"); - Set input = new HashSet(); + Set input = new HashSet<>(); input.add("4"); input.add("5"); - Map input2 = new HashMap(); + Map input2 = new HashMap<>(); input2.put("4", "5"); input2.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -503,7 +504,7 @@ public class BeanFactoryGenericsTests { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setFactoryMethodName("createInstance"); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("4", "5"); input.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -523,10 +524,10 @@ public class BeanFactoryGenericsTests { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setFactoryMethodName("createInstance"); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("1", "0"); input.put("2", "3"); - Map input2 = new HashMap(); + Map input2 = new HashMap<>(); input2.put("4", "5"); input2.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -547,7 +548,7 @@ public class BeanFactoryGenericsTests { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setFactoryMethodName("createInstance"); - Map input = new HashMap(); + Map input = new HashMap<>(); input.put("4", "5"); input.put("6", "7"); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -571,11 +572,11 @@ public class BeanFactoryGenericsTests { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setFactoryMethodName("createInstance"); - Map> input = new HashMap>(); - HashSet value1 = new HashSet(); + Map> input = new HashMap<>(); + HashSet value1 = new HashSet<>(); value1.add(new Integer(1)); input.put("1", value1); - ArrayList value2 = new ArrayList(); + ArrayList value2 = new ArrayList<>(); value2.add(Boolean.TRUE); input.put("2", value2); rbd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE); @@ -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()); }