Skip to content

Commit 384dc2a

Browse files
committed
Consistently use singleton lock for FactoryBean processing
Closes gh-33972
1 parent edf7f3c commit 384dc2a

File tree

3 files changed

+83
-70
lines changed

3 files changed

+83
-70
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

+46-40
Original file line numberDiff line numberDiff line change
@@ -991,54 +991,60 @@ protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd,
991991
*/
992992
@Nullable
993993
private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
994-
BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
995-
if (bw != null) {
996-
return (FactoryBean<?>) bw.getWrappedInstance();
997-
}
998-
Object beanInstance = getSingleton(beanName, false);
999-
if (beanInstance instanceof FactoryBean<?> factoryBean) {
1000-
return factoryBean;
1001-
}
1002-
if (isSingletonCurrentlyInCreation(beanName) ||
1003-
(mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
1004-
return null;
1005-
}
1006-
1007-
Object instance;
994+
this.singletonLock.lock();
1008995
try {
1009-
// Mark this bean as currently in creation, even if just partially.
1010-
beforeSingletonCreation(beanName);
1011-
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
1012-
instance = resolveBeforeInstantiation(beanName, mbd);
1013-
if (instance == null) {
1014-
bw = createBeanInstance(beanName, mbd, null);
1015-
instance = bw.getWrappedInstance();
1016-
this.factoryBeanInstanceCache.put(beanName, bw);
996+
BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
997+
if (bw != null) {
998+
return (FactoryBean<?>) bw.getWrappedInstance();
1017999
}
1018-
}
1019-
catch (UnsatisfiedDependencyException ex) {
1020-
// Don't swallow, probably misconfiguration...
1021-
throw ex;
1022-
}
1023-
catch (BeanCreationException ex) {
1024-
// Don't swallow a linkage error since it contains a full stacktrace on
1025-
// first occurrence... and just a plain NoClassDefFoundError afterwards.
1026-
if (ex.contains(LinkageError.class)) {
1000+
Object beanInstance = getSingleton(beanName, false);
1001+
if (beanInstance instanceof FactoryBean<?> factoryBean) {
1002+
return factoryBean;
1003+
}
1004+
if (isSingletonCurrentlyInCreation(beanName) ||
1005+
(mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
1006+
return null;
1007+
}
1008+
1009+
Object instance;
1010+
try {
1011+
// Mark this bean as currently in creation, even if just partially.
1012+
beforeSingletonCreation(beanName);
1013+
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
1014+
instance = resolveBeforeInstantiation(beanName, mbd);
1015+
if (instance == null) {
1016+
bw = createBeanInstance(beanName, mbd, null);
1017+
instance = bw.getWrappedInstance();
1018+
this.factoryBeanInstanceCache.put(beanName, bw);
1019+
}
1020+
}
1021+
catch (UnsatisfiedDependencyException ex) {
1022+
// Don't swallow, probably misconfiguration...
10271023
throw ex;
10281024
}
1029-
// Instantiation failure, maybe too early...
1030-
if (logger.isDebugEnabled()) {
1031-
logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex);
1025+
catch (BeanCreationException ex) {
1026+
// Don't swallow a linkage error since it contains a full stacktrace on
1027+
// first occurrence... and just a plain NoClassDefFoundError afterwards.
1028+
if (ex.contains(LinkageError.class)) {
1029+
throw ex;
1030+
}
1031+
// Instantiation failure, maybe too early...
1032+
if (logger.isDebugEnabled()) {
1033+
logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex);
1034+
}
1035+
onSuppressedException(ex);
1036+
return null;
10321037
}
1033-
onSuppressedException(ex);
1034-
return null;
1038+
finally {
1039+
// Finished partial creation of this bean.
1040+
afterSingletonCreation(beanName);
1041+
}
1042+
1043+
return getFactoryBean(beanName, instance);
10351044
}
10361045
finally {
1037-
// Finished partial creation of this bean.
1038-
afterSingletonCreation(beanName);
1046+
this.singletonLock.unlock();
10391047
}
1040-
1041-
return getFactoryBean(beanName, instance);
10421048
}
10431049

10441050
/**

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
7676
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
7777

7878

79+
/** Common lock for singleton creation. */
80+
final Lock singletonLock = new ReentrantLock();
81+
7982
/** Cache of singleton objects: bean name to bean instance. */
8083
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
8184

@@ -91,8 +94,6 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
9194
/** Set of registered singletons, containing the bean names in registration order. */
9295
private final Set<String> registeredSingletons = Collections.synchronizedSet(new LinkedHashSet<>(256));
9396

94-
private final Lock singletonLock = new ReentrantLock();
95-
9697
/** Names of beans that are currently in creation. */
9798
private final Set<String> singletonsCurrentlyInCreation = ConcurrentHashMap.newKeySet(16);
9899

spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java

+34-28
Original file line numberDiff line numberDiff line change
@@ -118,39 +118,45 @@ protected Object getCachedObjectForFactoryBean(String beanName) {
118118
*/
119119
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
120120
if (factory.isSingleton() && containsSingleton(beanName)) {
121-
Object object = this.factoryBeanObjectCache.get(beanName);
122-
if (object == null) {
123-
object = doGetObjectFromFactoryBean(factory, beanName);
124-
// Only post-process and store if not put there already during getObject() call above
125-
// (for example, because of circular reference processing triggered by custom getBean calls)
126-
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
127-
if (alreadyThere != null) {
128-
object = alreadyThere;
129-
}
130-
else {
131-
if (shouldPostProcess) {
132-
if (isSingletonCurrentlyInCreation(beanName)) {
133-
// Temporarily return non-post-processed object, not storing it yet
134-
return object;
135-
}
136-
beforeSingletonCreation(beanName);
137-
try {
138-
object = postProcessObjectFromFactoryBean(object, beanName);
139-
}
140-
catch (Throwable ex) {
141-
throw new BeanCreationException(beanName,
142-
"Post-processing of FactoryBean's singleton object failed", ex);
121+
this.singletonLock.lock();
122+
try {
123+
Object object = this.factoryBeanObjectCache.get(beanName);
124+
if (object == null) {
125+
object = doGetObjectFromFactoryBean(factory, beanName);
126+
// Only post-process and store if not put there already during getObject() call above
127+
// (for example, because of circular reference processing triggered by custom getBean calls)
128+
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
129+
if (alreadyThere != null) {
130+
object = alreadyThere;
131+
}
132+
else {
133+
if (shouldPostProcess) {
134+
if (isSingletonCurrentlyInCreation(beanName)) {
135+
// Temporarily return non-post-processed object, not storing it yet
136+
return object;
137+
}
138+
beforeSingletonCreation(beanName);
139+
try {
140+
object = postProcessObjectFromFactoryBean(object, beanName);
141+
}
142+
catch (Throwable ex) {
143+
throw new BeanCreationException(beanName,
144+
"Post-processing of FactoryBean's singleton object failed", ex);
145+
}
146+
finally {
147+
afterSingletonCreation(beanName);
148+
}
143149
}
144-
finally {
145-
afterSingletonCreation(beanName);
150+
if (containsSingleton(beanName)) {
151+
this.factoryBeanObjectCache.put(beanName, object);
146152
}
147153
}
148-
if (containsSingleton(beanName)) {
149-
this.factoryBeanObjectCache.put(beanName, object);
150-
}
151154
}
155+
return object;
156+
}
157+
finally {
158+
this.singletonLock.unlock();
152159
}
153-
return object;
154160
}
155161
else {
156162
Object object = doGetObjectFromFactoryBean(factory, beanName);

0 commit comments

Comments
 (0)