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 5bd6b182322d4b45f9eacd0772bc656f84bdccaf..f616f4a3970bed22ea392c6bcd554e6de8be7fae 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 @@ -739,28 +739,37 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac final Holder objectType = new Holder(); String factoryBeanName = mbd.getFactoryBeanName(); final 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 && ((AbstractBeanDefinition) fbDef).hasBeanClass()) { - // CGLIB subclass methods hide generic parameters; look at the original user class. - Class fbClass = ClassUtils.getUserClass(((AbstractBeanDefinition) fbDef).getBeanClass()); - // Find the given factory method, taking into account that in the case of - // @Bean methods, there may be parameters present. - ReflectionUtils.doWithMethods(fbClass, - new ReflectionUtils.MethodCallback() { - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - if (method.getName().equals(factoryMethodName) && - FactoryBean.class.isAssignableFrom(method.getReturnType())) { - objectType.value = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class); + + if (factoryBeanName != null) { + if (factoryMethodName != null) { + // Try to obtain the FactoryBean's object type without instantiating it at all. + BeanDefinition fbDef = getBeanDefinition(factoryBeanName); + if (fbDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) fbDef).hasBeanClass()) { + // CGLIB subclass methods hide generic parameters; look at the original user class. + Class fbClass = ClassUtils.getUserClass(((AbstractBeanDefinition) fbDef).getBeanClass()); + // Find the given factory method, taking into account that in the case of + // @Bean methods, there may be parameters present. + ReflectionUtils.doWithMethods(fbClass, + new ReflectionUtils.MethodCallback() { + @Override + public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + if (method.getName().equals(factoryMethodName) && + FactoryBean.class.isAssignableFrom(method.getReturnType())) { + objectType.value = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class); + } } - } - }); - if (objectType.value != null) { - return objectType.value; + }); + if (objectType.value != null) { + return objectType.value; + } } } + // If not resolvable above and the referenced factory bean doesn't exist yet, + // exit here - we don't want to force the creation of another bean just to + // obtain a FactoryBean's object type... + if (!isBeanEligibleForMetadataCaching(factoryBeanName)) { + return null; + } } FactoryBean fb = (mbd.isSingleton() ? diff --git a/spring-context/src/test/java/org/springframework/context/annotation/Spr11202Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/Spr11202Tests.java new file mode 100644 index 0000000000000000000000000000000000000000..b8a1204fdcc885105bfe0fbeb9fa0068affc1c81 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/Spr11202Tests.java @@ -0,0 +1,149 @@ +/* + * Copyright 2002-2013 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 java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.Assert; + +import static org.junit.Assert.*; + +/** + * @author Dave Syer + */ +public class Spr11202Tests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (context != null) { + context.close(); + } + } + + @Test // Fails + public void testWithImporter() { + context = new AnnotationConfigApplicationContext(Wrapper.class); + assertEquals("foo", context.getBean("value")); + } + + @Test // Passes + public void testWithoutImporter() { + context = new AnnotationConfigApplicationContext(Config.class); + assertEquals("foo", context.getBean("value")); + } + + + @Configuration + @Import(Selector.class) + protected static class Wrapper { + } + + protected static class Selector implements ImportSelector { + + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + return new String[] {Config.class.getName()}; + } + } + + @Configuration + protected static class Config { + + @Bean + public FooFactoryBean foo() { + return new FooFactoryBean(); + } + + @Bean + public String value() throws Exception { + String name = foo().getObject().getName(); + Assert.state(name != null, "Name cannot be null"); + return name; + } + + @Bean + @Conditional(NoBarCondition.class) + public String bar() throws Exception { + return "bar"; + } + } + + protected static class NoBarCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + if (context.getBeanFactory().getBeanNamesForAnnotation(Bar.class).length > 0) { + return false; + } + return true; + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Target(ElementType.TYPE) + protected static @interface Bar { + } + + protected static class FooFactoryBean implements FactoryBean, InitializingBean { + + private Foo foo = new Foo(); + + @Override + public Foo getObject() throws Exception { + return foo; + } + + @Override + public Class getObjectType() { + return Foo.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + @Override + public void afterPropertiesSet() throws Exception { + this.foo.name = "foo"; + } + } + + protected static class Foo { + + private String name; + + public String getName() { + return name; + } + } + +} diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java index 7e11066fe836898e90f1c5237f9da7c879be19c1..c1a09ec3358599695bfa7b0186289f82a40b5b31 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java @@ -80,6 +80,7 @@ public class ConfigurationClassProcessingTests { RequiredAnnotationBeanPostProcessor rapp = new RequiredAnnotationBeanPostProcessor(); rapp.setBeanFactory(factory); factory.addBeanPostProcessor(rapp); + factory.freezeConfiguration(); return factory; }