diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java index 97250ad91fb..0da024b6ae2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java @@ -31,8 +31,11 @@ import org.jspecify.annotations.Nullable; *

As of 7.0, this interface also allows for exposing additional object * types for dependency injection through implementing a pair of methods: * {@link #getObject(Class)} as well as {@link #supportsType(Class)}. - * The primary {@link #getObjectType()} will be exposed for regular access. - * Only if a specific type is requested, additional types are considered. + * The primary {@link #getObjectType()} will be exposed for regular access; + * only if a specific type is requested, additional types are considered. + * The container will not cache {@code SmartFactoryBean}-produced objects; + * make sure that the {@code getObject} implementation is thread-safe for + * repeated invocations. * *

NOTE: This interface is a special purpose interface, mainly for * internal use within the framework and within collaborating frameworks. 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 80823e01a01..3e428b976fa 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 @@ -130,49 +130,40 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg locked = (lockFlag && this.singletonLock.tryLock()); } try { - // A SmartFactoryBean may return multiple object types -> do not cache. - boolean smart = (factory instanceof SmartFactoryBean); - // Defensively synchronize against non-thread-safe FactoryBean.getObject() implementations, - // potentially to be called from a background thread while the main thread currently calls - // the same getObject() method within the singleton lock. - synchronized (factory) { - Object object = (!smart ? this.factoryBeanObjectCache.get(beanName) : null); - if (object == null) { - object = doGetObjectFromFactoryBean(factory, requiredType, beanName); - // Only post-process and store if not put there already during getObject() call above - // (for example, because of circular reference processing triggered by custom getBean calls) - Object alreadyThere = (!smart ? this.factoryBeanObjectCache.get(beanName) : null); - if (alreadyThere != null) { - object = alreadyThere; - } - else { - if (shouldPostProcess) { - if (locked) { - if (isSingletonCurrentlyInCreation(beanName)) { - // Temporarily return non-post-processed object, not storing it yet - return object; - } - beforeSingletonCreation(beanName); - } - try { - object = postProcessObjectFromFactoryBean(object, beanName); - } - catch (Throwable ex) { - throw new BeanCreationException(beanName, - "Post-processing of FactoryBean's singleton object failed", ex); + if (factory instanceof SmartFactoryBean) { + // A SmartFactoryBean may return multiple object types -> do not cache. + // Also, a SmartFactoryBean needs to be thread-safe -> no synchronization necessary. + Object object = doGetObjectFromFactoryBean(factory, requiredType, beanName); + if (shouldPostProcess) { + object = postProcessObjectFromSingletonFactoryBean(object, beanName, locked); + } + return object; + } + else { + // Defensively synchronize against non-thread-safe FactoryBean.getObject() implementations, + // potentially to be called from a background thread while the main thread currently calls + // the same getObject() method within the singleton lock. + synchronized (factory) { + Object object = this.factoryBeanObjectCache.get(beanName); + if (object == null) { + object = doGetObjectFromFactoryBean(factory, requiredType, beanName); + // Only post-process and store if not put there already during getObject() call above + // (for example, because of circular reference processing triggered by custom getBean calls) + Object alreadyThere = this.factoryBeanObjectCache.get(beanName); + if (alreadyThere != null) { + object = alreadyThere; + } + else { + if (shouldPostProcess) { + object = postProcessObjectFromSingletonFactoryBean(object, beanName, locked); } - finally { - if (locked) { - afterSingletonCreation(beanName); - } + if (containsSingleton(beanName)) { + this.factoryBeanObjectCache.put(beanName, object); } } - if (!smart && containsSingleton(beanName)) { - this.factoryBeanObjectCache.put(beanName, object); - } } + return object; } - return object; } } finally { @@ -230,6 +221,31 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg return object; } + /** + * Post-process the given object instance produced by a singleton FactoryBean. + */ + private Object postProcessObjectFromSingletonFactoryBean(Object object, String beanName, boolean locked) { + if (locked) { + if (isSingletonCurrentlyInCreation(beanName)) { + // Temporarily return non-post-processed object, not storing it yet + return object; + } + beforeSingletonCreation(beanName); + } + try { + return postProcessObjectFromFactoryBean(object, beanName); + } + catch (Throwable ex) { + throw new BeanCreationException(beanName, + "Post-processing of FactoryBean's singleton object failed", ex); + } + finally { + if (locked) { + afterSingletonCreation(beanName); + } + } + } + /** * Post-process the given object that has been obtained from the FactoryBean. * The resulting object will get exposed for bean references.