diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
index af81eeb46ba..1a4c9874369 100644
--- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
+++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
@@ -21,6 +21,9 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
@@ -68,6 +71,7 @@ import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.core.GenericTypeResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
@@ -651,7 +655,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
}
/**
- * This implementation checks the FactoryBean's getObjectType method
+ * This implementation attempts to query the FactoryBean's generic parameter metadata
+ * if present to determin the object type. If not present, i.e. the FactoryBean is
+ * declared as a raw type, checks the FactoryBean's getObjectType method
* on a plain instance of the FactoryBean, without bean properties applied yet.
* If this doesn't return a type yet, a full creation of the FactoryBean is
* used as fallback (through delegation to the superclass's implementation).
@@ -660,14 +666,34 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
* it will be fully created to check the type of its exposed object.
*/
@Override
- protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
- FactoryBean fb = (mbd.isSingleton() ?
+ protected Class> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
+ Class> objectType = null;
+ String factoryBeanName = mbd.getFactoryBeanName();
+ String factoryMethodName = mbd.getFactoryMethodName();
+ if (factoryBeanName != null && factoryMethodName != null) {
+ // Try to obtain the FactoryBean's object type without instantiating it at all.
+ BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
+ if (fbDef instanceof AbstractBeanDefinition) {
+ Class> fbClass = ((AbstractBeanDefinition)fbDef).getBeanClass();
+ if (ClassUtils.isCglibProxyClass(fbClass)) {
+ // CGLIB subclass methods hide generic parameters. look at the superclass.
+ fbClass = fbClass.getSuperclass();
+ }
+ Method m = ReflectionUtils.findMethod(fbClass, factoryMethodName);
+ objectType = GenericTypeResolver.resolveReturnTypeArgument(m, FactoryBean.class);
+ if (objectType != null) {
+ return objectType;
+ }
+ }
+ }
+
+ FactoryBean> fb = (mbd.isSingleton() ?
getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));
if (fb != null) {
// Try to obtain the FactoryBean's object type from this early stage of the instance.
- Class objectType = getTypeForFactoryBean(fb);
+ objectType = getTypeForFactoryBean(fb);
if (objectType != null) {
return objectType;
}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanAndAutowiringTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanAndAutowiringTests.java
new file mode 100755
index 00000000000..14d1932dca6
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanAndAutowiringTests.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2002-2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.context.annotation;
+
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.Assert;
+
+/**
+ * Tests cornering bug SPR-8514.
+ *
+ * @author Chris Beams
+ * @since 3.1
+ */
+public class ConfigurationWithFactoryBeanAndAutowiringTests {
+
+ @Test
+ public void withConcreteFactoryBeanImplementationAsReturnType() {
+ AnnotationConfigApplicationContext ctx =
+ new AnnotationConfigApplicationContext();
+ ctx.register(AppConfig.class);
+ ctx.register(ConcreteFactoryBeanImplementationConfig.class);
+ ctx.refresh();
+ }
+
+ @Test
+ public void withParameterizedFactoryBeanImplementationAsReturnType() {
+ AnnotationConfigApplicationContext ctx =
+ new AnnotationConfigApplicationContext();
+ ctx.register(AppConfig.class);
+ ctx.register(ParameterizedFactoryBeanImplementationConfig.class);
+ ctx.refresh();
+ }
+
+ @Test
+ public void withParameterizedFactoryBeanInterfaceAsReturnType() {
+ AnnotationConfigApplicationContext ctx =
+ new AnnotationConfigApplicationContext();
+ ctx.register(AppConfig.class);
+ ctx.register(ParameterizedFactoryBeanInterfaceConfig.class);
+ ctx.refresh();
+ }
+
+ @Test
+ public void withNonPublicParameterizedFactoryBeanInterfaceAsReturnType() {
+ AnnotationConfigApplicationContext ctx =
+ new AnnotationConfigApplicationContext();
+ ctx.register(AppConfig.class);
+ ctx.register(NonPublicParameterizedFactoryBeanInterfaceConfig.class);
+ ctx.refresh();
+ }
+
+ @Test(expected=BeanCreationException.class)
+ public void withRawFactoryBeanInterfaceAsReturnType() {
+ AnnotationConfigApplicationContext ctx =
+ new AnnotationConfigApplicationContext();
+ ctx.register(AppConfig.class);
+ ctx.register(RawFactoryBeanInterfaceConfig.class);
+ ctx.refresh();
+ }
+
+ @Test(expected=BeanCreationException.class)
+ public void withWildcardParameterizedFactoryBeanInterfaceAsReturnType() {
+ AnnotationConfigApplicationContext ctx =
+ new AnnotationConfigApplicationContext();
+ ctx.register(AppConfig.class);
+ ctx.register(WildcardParameterizedFactoryBeanInterfaceConfig.class);
+ ctx.refresh();
+ }
+
+}
+
+
+class DummyBean {
+}
+
+
+class MyFactoryBean implements FactoryBean {
+ public String getObject() throws Exception {
+ return "foo";
+ }
+ public Class getObjectType() {
+ return String.class;
+ }
+ public boolean isSingleton() {
+ return true;
+ }
+}
+
+
+class MyParameterizedFactoryBean implements FactoryBean {
+
+ private final T obj;
+
+ public MyParameterizedFactoryBean(T obj) {
+ this.obj = obj;
+ }
+
+ public T getObject() throws Exception {
+ return obj;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Class getObjectType() {
+ return (Class)obj.getClass();
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+}
+
+
+@Configuration
+class AppConfig {
+ @Bean
+ public DummyBean dummyBean() {
+ return new DummyBean();
+ }
+}
+
+
+@Configuration
+class ConcreteFactoryBeanImplementationConfig {
+ @Autowired
+ private DummyBean dummyBean;
+
+ @Bean
+ public MyFactoryBean factoryBean() {
+ Assert.notNull(dummyBean, "DummyBean was not injected.");
+ return new MyFactoryBean();
+ }
+}
+
+
+@Configuration
+class ParameterizedFactoryBeanImplementationConfig {
+ @Autowired
+ private DummyBean dummyBean;
+
+ @Bean
+ public MyParameterizedFactoryBean factoryBean() {
+ Assert.notNull(dummyBean, "DummyBean was not injected.");
+ return new MyParameterizedFactoryBean("whatev");
+ }
+}
+
+
+@Configuration
+class ParameterizedFactoryBeanInterfaceConfig {
+ @Autowired
+ private DummyBean dummyBean;
+
+ @Bean
+ public FactoryBean factoryBean() {
+ Assert.notNull(dummyBean, "DummyBean was not injected.");
+ return new MyFactoryBean();
+ }
+}
+
+
+@Configuration
+class NonPublicParameterizedFactoryBeanInterfaceConfig {
+ @Autowired
+ private DummyBean dummyBean;
+
+ @Bean
+ FactoryBean factoryBean() {
+ Assert.notNull(dummyBean, "DummyBean was not injected.");
+ return new MyFactoryBean();
+ }
+}
+
+
+@Configuration
+class RawFactoryBeanInterfaceConfig {
+ @Autowired
+ private DummyBean dummyBean;
+
+ @Bean
+ @SuppressWarnings("rawtypes")
+ public FactoryBean factoryBean() {
+ Assert.notNull(dummyBean, "DummyBean was not injected.");
+ return new MyFactoryBean();
+ }
+}
+
+
+@Configuration
+class WildcardParameterizedFactoryBeanInterfaceConfig {
+ @Autowired
+ private DummyBean dummyBean;
+
+ @Bean
+ public FactoryBean> factoryBean() {
+ Assert.notNull(dummyBean, "DummyBean was not injected.");
+ return new MyFactoryBean();
+ }
+}