No synchronization (and no caching) for SmartFactoryBean invocations

See gh-35545
See gh-35101
This commit is contained in:
Juergen Hoeller 2025-10-03 17:56:11 +02:00
parent 2170b40213
commit e2168edc8f
2 changed files with 62 additions and 43 deletions

View File

@ -31,8 +31,11 @@ import org.jspecify.annotations.Nullable;
* <p>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.
*
* <p><b>NOTE:</b> This interface is a special purpose interface, mainly for
* internal use within the framework and within collaborating frameworks.

View File

@ -130,50 +130,41 @@ 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);
}
finally {
if (locked) {
afterSingletonCreation(beanName);
}
}
}
if (!smart && containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
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);
}
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
}
finally {
if (locked) {
@ -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.