diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java index 559145268b31..054ff22e86ad 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java @@ -97,6 +97,11 @@ public final AnnotationMetadata getMetadata() { return this.metadata; } + @Override + public String getFactoryMethodReturnType() { + return (this.factoryMethodMetadata == null ? null : this.factoryMethodMetadata.getReturnTypeName()); + } + @Override public final MethodMetadata getFactoryMethodMetadata() { return this.factoryMethodMetadata; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java index 535d2a8e9611..d217f6256cb7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java @@ -120,6 +120,11 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { */ String getFactoryMethodName(); + /** + * Return the class name of the factory method return type, if known in advance. + */ + String getFactoryMethodReturnType(); + /** * Specify a factory method, if any. This method will be invoked with * constructor arguments, or with no arguments if none are specified. 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 a3fc160fe35f..6536548ddd44 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 @@ -60,6 +60,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.UnsatisfiedDependencyException; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; @@ -74,6 +75,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.PriorityOrdered; +import org.springframework.core.type.MethodMetadata; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -653,24 +655,25 @@ protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition m return preResolved; } - Class factoryClass; - boolean isStatic = true; - String factoryBeanName = mbd.getFactoryBeanName(); - if (factoryBeanName != null) { - if (factoryBeanName.equals(beanName)) { - throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, - "factory-bean reference points back to the same bean definition"); + boolean isStatic = (factoryBeanName == null); + + // Use the predetermined return type if possible + if (factoryBeanName != null && mbd.getFactoryMethodReturnType() != null) { + if (mbd.getFactoryMethodReturnType() != null) { + try { + if (mbd.factoryMethodReturnType == null) { + mbd.factoryMethodReturnType = ClassUtils.forName( + mbd.getFactoryMethodReturnType(), getBeanClassLoader()); + } + return mbd.factoryMethodReturnType; + } + catch (Exception ex) { + } } - // Check declared factory method return type on factory class. - factoryClass = getType(factoryBeanName); - isStatic = false; - } - else { - // Check declared factory method return type on bean class. - factoryClass = resolveBeanClass(mbd, beanName, typesToMatch); } + Class factoryClass = getFactoryClass(beanName, mbd, factoryBeanName, typesToMatch); if (factoryClass == null) { return null; } @@ -689,29 +692,7 @@ protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition m if (factoryMethod.getTypeParameters().length > 0) { try { // Fully resolve parameter names and argument values. - Class[] paramTypes = factoryMethod.getParameterTypes(); - String[] paramNames = null; - ParameterNameDiscoverer pnd = getParameterNameDiscoverer(); - if (pnd != null) { - paramNames = pnd.getParameterNames(factoryMethod); - } - ConstructorArgumentValues cav = mbd.getConstructorArgumentValues(); - Set usedValueHolders = - new HashSet(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()); + Class returnType = getReturnType(mbd, factoryMethod); if (returnType != null) { cache = true; commonType = ClassUtils.determineCommonAncestor(returnType, commonType); @@ -730,16 +711,61 @@ protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition m } if (commonType != null) { + if(mbd.resolvedFactoryMethodReturnType != null && !mbd.resolvedFactoryMethodReturnType.equals(commonType)) { + throw new IllegalStateException(commonType+" "+mbd.factoryMethodReturnType ); + } // Clear return type found: all factory methods return same type. if (cache) { mbd.resolvedFactoryMethodReturnType = commonType; } return commonType; } - else { - // Ambiguous return types found: return null to indicate "not determinable". - return null; + // Ambiguous return types found: return null to indicate "not determinable". + return null; + } + + private Class getFactoryClass(String beanName, RootBeanDefinition mbd, String factoryBeanName, + Class... typesToMatch) { + if (factoryBeanName != null) { + if (factoryBeanName.equals(beanName)) { + throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, + "factory-bean reference points back to the same bean definition"); + } + // Check declared factory method return type on factory class. + return getType(factoryBeanName); + } + // Check declared factory method return type on bean class. + return resolveBeanClass(mbd, beanName, typesToMatch); + } + + private Class getReturnType(RootBeanDefinition mbd, Method factoryMethod) { + Class[] paramTypes = factoryMethod.getParameterTypes(); + String[] paramNames = getParameterNames(factoryMethod); + ConstructorArgumentValues cav = mbd.getConstructorArgumentValues(); + Set usedValueHolders = + new HashSet(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); + } + } + return AutowireUtils.resolveReturnTypeForFactoryMethod( + factoryMethod, args, getBeanClassLoader()); + } + + private String[] getParameterNames(Method factoryMethod) { + ParameterNameDiscoverer discoverer = getParameterNameDiscoverer(); + if (discoverer != null) { + return discoverer.getParameterNames(factoryMethod); } + return null; } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index 33f20bbac470..18f600b078c7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -758,6 +758,11 @@ public String getFactoryMethodName() { return this.factoryMethodName; } + @Override + public String getFactoryMethodReturnType() { + return null; + } + /** * Set the name of the initializer method. The default is {@code null} * in which case there is no initializer method. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index d2ac971cbc9f..c768af6bc8dc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -61,6 +61,9 @@ public class RootBeanDefinition extends AbstractBeanDefinition { /** Package-visible field for caching the resolved constructor or factory method */ Object resolvedConstructorOrFactoryMethod; + /** Package-visible field for caching the loaded factory method return type */ + volatile Class factoryMethodReturnType; + /** Package-visible field for caching the return type of a generically typed factory method */ volatile Class resolvedFactoryMethodReturnType; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 7af760f41c43..e1b2f9d88c27 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -26,7 +26,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; @@ -402,6 +401,11 @@ public AnnotationMetadata getMetadata() { return this.annotationMetadata; } + @Override + public String getFactoryMethodReturnType() { + return (this.factoryMethodMetadata == null ? null : this.factoryMethodMetadata.getReturnTypeName()); + } + @Override public MethodMetadata getFactoryMethodMetadata() { return this.factoryMethodMetadata;