diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java
index afda1d4c60..53dc17657e 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -90,7 +90,7 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
* Obtain an object to expose from the given FactoryBean.
* @param factory the FactoryBean instance
* @param beanName the name of the bean
- * @param shouldPostProcess whether the bean is subject for post-processing
+ * @param shouldPostProcess whether the bean is subject to post-processing
* @return the object obtained from the FactoryBean
* @throws BeanCreationException if FactoryBean object creation failed
* @see org.springframework.beans.factory.FactoryBean#getObject()
@@ -100,14 +100,40 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
synchronized (getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
- object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
- this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
+ object = doGetObjectFromFactoryBean(factory, beanName);
+ // Only post-process and store if not put there already during getObject() call above
+ // (e.g. because of circular reference processing triggered by custom getBean calls)
+ Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
+ if (alreadyThere != null) {
+ object = alreadyThere;
+ }
+ else {
+ if (object != null && shouldPostProcess) {
+ try {
+ object = postProcessObjectFromFactoryBean(object, beanName);
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(beanName,
+ "Post-processing of FactoryBean's singleton object failed", ex);
+ }
+ }
+ this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
+ }
}
return (object != NULL_OBJECT ? object : null);
}
}
else {
- return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
+ Object object = doGetObjectFromFactoryBean(factory, beanName);
+ if (object != null && shouldPostProcess) {
+ try {
+ object = postProcessObjectFromFactoryBean(object, beanName);
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
+ }
+ }
+ return object;
}
}
@@ -115,13 +141,11 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
* Obtain an object to expose from the given FactoryBean.
* @param factory the FactoryBean instance
* @param beanName the name of the bean
- * @param shouldPostProcess whether the bean is subject for post-processing
* @return the object obtained from the FactoryBean
* @throws BeanCreationException if FactoryBean object creation failed
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
- private Object doGetObjectFromFactoryBean(
- final FactoryBean> factory, final String beanName, final boolean shouldPostProcess)
+ private Object doGetObjectFromFactoryBean(final FactoryBean> factory, final String beanName)
throws BeanCreationException {
Object object;
@@ -151,23 +175,12 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
-
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
if (object == null && isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
-
- if (object != null && shouldPostProcess) {
- try {
- object = postProcessObjectFromFactoryBean(object, beanName);
- }
- catch (Throwable ex) {
- throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
- }
- }
-
return object;
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests-circular.xml b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests-circular.xml
new file mode 100644
index 0000000000..1c328aa8d4
--- /dev/null
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests-circular.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java
index 5d7808f426..776cb3763e 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -16,19 +16,24 @@
package org.springframework.beans.factory;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.springframework.tests.TestResourceUtils.qualifiedResource;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
+
+import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
+import static org.junit.Assert.*;
+import static org.springframework.tests.TestResourceUtils.*;
+
/**
* @author Rob Harrop
* @author Juergen Hoeller
@@ -40,6 +45,7 @@ public final class FactoryBeanTests {
private static final Resource RETURNS_NULL_CONTEXT = qualifiedResource(CLASS, "returnsNull.xml");
private static final Resource WITH_AUTOWIRING_CONTEXT = qualifiedResource(CLASS, "withAutowiring.xml");
private static final Resource ABSTRACT_CONTEXT = qualifiedResource(CLASS, "abstract.xml");
+ private static final Resource CIRCULAR_CONTEXT = qualifiedResource(CLASS, "circular.xml");
@Test
public void testFactoryBeanReturnsNull() throws Exception {
@@ -96,6 +102,23 @@ public final class FactoryBeanTests {
factory.getBeansOfType(AbstractFactoryBean.class);
}
+ @Test
+ public void testCircularReferenceWithPostProcessor() {
+ DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
+ new XmlBeanDefinitionReader(factory).loadBeanDefinitions(CIRCULAR_CONTEXT);
+
+ CountingPostProcessor counter = new CountingPostProcessor();
+ factory.addBeanPostProcessor(counter);
+
+ BeanImpl1 impl1 = factory.getBean(BeanImpl1.class);
+ assertNotNull(impl1);
+ assertNotNull(impl1.getImpl2());
+ assertNotNull(impl1.getImpl2());
+ assertSame(impl1, impl1.getImpl2().getImpl1());
+ assertEquals(1, counter.getCount("bean1"));
+ assertEquals(1, counter.getCount("bean2"));
+ }
+
public static class NullReturningFactoryBean implements FactoryBean