No synchronization (and no caching) for SmartFactoryBean invocations
See gh-35545 See gh-35101
This commit is contained in:
parent
2170b40213
commit
e2168edc8f
|
@ -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.
|
||||
|
|
|
@ -130,44 +130,34 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
locked = (lockFlag && this.singletonLock.tryLock());
|
||||
}
|
||||
try {
|
||||
if (factory instanceof SmartFactoryBean<?>) {
|
||||
// A SmartFactoryBean may return multiple object types -> do not cache.
|
||||
boolean smart = (factory instanceof SmartFactoryBean<?>);
|
||||
// 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 = (!smart ? this.factoryBeanObjectCache.get(beanName) : null);
|
||||
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 = (!smart ? this.factoryBeanObjectCache.get(beanName) : null);
|
||||
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
|
||||
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;
|
||||
object = postProcessObjectFromSingletonFactoryBean(object, beanName, locked);
|
||||
}
|
||||
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)) {
|
||||
if (containsSingleton(beanName)) {
|
||||
this.factoryBeanObjectCache.put(beanName, object);
|
||||
}
|
||||
}
|
||||
|
@ -175,6 +165,7 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
return object;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (locked) {
|
||||
this.singletonLock.unlock();
|
||||
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue