Skip to content

Commit f6209cd

Browse files
committed
DefaultSingletonBeanRegistry avoids singletonObjects lock wherever possible for non-singleton factory performance
Also fixing setCurrentlyInCreation to use a concurrent Set and to apply to prototype beans as well. Issue: SPR-9819
1 parent de91d75 commit f6209cd

File tree

4 files changed

+62
-52
lines changed

4 files changed

+62
-52
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -321,8 +321,8 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
321321
boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException;
322322

323323
/**
324-
* Explicitly control in-creation status of the specified bean. For
325-
* container internal use only.
324+
* Explicitly control the current in-creation status of the specified bean.
325+
* For container-internal use only.
326326
* @param beanName the name of the bean
327327
* @param inCreation whether the bean is currently in creation
328328
* @since 3.1

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,22 @@ else if (containsSingleton(beanName)) {
897897
return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
898898
}
899899

900+
@Override
901+
public boolean isActuallyInCreation(String beanName) {
902+
return isSingletonCurrentlyInCreation(beanName) || isPrototypeCurrentlyInCreation(beanName);
903+
}
904+
905+
/**
906+
* Return whether the specified prototype bean is currently in creation
907+
* (within the current thread).
908+
* @param beanName the name of the bean
909+
*/
910+
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
911+
Object curVal = this.prototypesCurrentlyInCreation.get();
912+
return (curVal != null &&
913+
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
914+
}
915+
900916
/**
901917
* Callback before prototype creation.
902918
* <p>The default implementation register the prototype as currently in creation.
@@ -942,22 +958,6 @@ else if (curVal instanceof Set) {
942958
}
943959
}
944960

945-
/**
946-
* Return whether the specified prototype bean is currently in creation
947-
* (within the current thread).
948-
* @param beanName the name of the bean
949-
*/
950-
protected final boolean isPrototypeCurrentlyInCreation(String beanName) {
951-
Object curVal = this.prototypesCurrentlyInCreation.get();
952-
return (curVal != null &&
953-
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
954-
}
955-
956-
public boolean isCurrentlyInCreation(String beanName) {
957-
Assert.notNull(beanName, "Bean name must not be null");
958-
return isSingletonCurrentlyInCreation(beanName) || isPrototypeCurrentlyInCreation(beanName);
959-
}
960-
961961
public void destroyBean(String beanName, Object beanInstance) {
962962
destroyBean(beanName, beanInstance, getMergedLocalBeanDefinition(beanName));
963963
}

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

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
package org.springframework.beans.factory.support;
1818

19-
import java.util.Collections;
2019
import java.util.HashMap;
21-
import java.util.HashSet;
2220
import java.util.Iterator;
2321
import java.util.LinkedHashMap;
2422
import java.util.LinkedHashSet;
@@ -94,11 +92,11 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
9492
/** Set of registered singletons, containing the bean names in registration order */
9593
private final Set<String> registeredSingletons = new LinkedHashSet<String>(16);
9694

97-
/** Names of beans that are currently in creation */
98-
private final Set<String> singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet<String>());
95+
/** Names of beans that are currently in creation (using a ConcurrentHashMap as a Set) */
96+
private final Map<String, Boolean> singletonsCurrentlyInCreation = new ConcurrentHashMap<String, Boolean>();
9997

100-
/** Names of beans currently excluded from in creation checks */
101-
private final Set<String> inCreationCheckExclusions = new HashSet<String>();
98+
/** Names of beans currently excluded from in creation checks (using a ConcurrentHashMap as a Set) */
99+
private final Map<String, Boolean> inCreationCheckExclusions = new ConcurrentHashMap<String, Boolean>();
102100

103101
/** List of suppressed Exceptions, available for associating related causes */
104102
private Set<Exception> suppressedExceptions;
@@ -166,7 +164,7 @@ protected void addSingletonFactory(String beanName, ObjectFactory singletonFacto
166164
}
167165

168166
public Object getSingleton(String beanName) {
169-
return getSingleton(beanName, isSingletonCurrentlyInCreation(beanName));
167+
return getSingleton(beanName, true);
170168
}
171169

172170
/**
@@ -179,10 +177,10 @@ public Object getSingleton(String beanName) {
179177
*/
180178
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
181179
Object singletonObject = this.singletonObjects.get(beanName);
182-
if (singletonObject == null && allowEarlyReference) {
180+
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
183181
synchronized (this.singletonObjects) {
184182
singletonObject = this.earlySingletonObjects.get(beanName);
185-
if (singletonObject == null) {
183+
if (singletonObject == null && allowEarlyReference) {
186184
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
187185
if (singletonFactory != null) {
188186
singletonObject = singletonFactory.getObject();
@@ -289,14 +287,43 @@ public int getSingletonCount() {
289287
}
290288

291289

290+
public void setCurrentlyInCreation(String beanName, boolean inCreation) {
291+
Assert.notNull(beanName, "Bean name must not be null");
292+
if (!inCreation) {
293+
this.inCreationCheckExclusions.put(beanName, Boolean.TRUE);
294+
}
295+
else {
296+
this.inCreationCheckExclusions.remove(beanName);
297+
}
298+
}
299+
300+
public boolean isCurrentlyInCreation(String beanName) {
301+
Assert.notNull(beanName, "Bean name must not be null");
302+
return (!this.inCreationCheckExclusions.containsKey(beanName) && isActuallyInCreation(beanName));
303+
}
304+
305+
protected boolean isActuallyInCreation(String beanName) {
306+
return isSingletonCurrentlyInCreation(beanName);
307+
}
308+
309+
/**
310+
* Return whether the specified singleton bean is currently in creation
311+
* (within the entire factory).
312+
* @param beanName the name of the bean
313+
*/
314+
public boolean isSingletonCurrentlyInCreation(String beanName) {
315+
return this.singletonsCurrentlyInCreation.containsKey(beanName);
316+
}
317+
292318
/**
293319
* Callback before singleton creation.
294320
* <p>Default implementation register the singleton as currently in creation.
295321
* @param beanName the name of the singleton about to be created
296322
* @see #isSingletonCurrentlyInCreation
297323
*/
298324
protected void beforeSingletonCreation(String beanName) {
299-
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
325+
if (!this.inCreationCheckExclusions.containsKey(beanName) &&
326+
this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) {
300327
throw new BeanCurrentlyInCreationException(beanName);
301328
}
302329
}
@@ -308,29 +335,12 @@ protected void beforeSingletonCreation(String beanName) {
308335
* @see #isSingletonCurrentlyInCreation
309336
*/
310337
protected void afterSingletonCreation(String beanName) {
311-
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
338+
if (!this.inCreationCheckExclusions.containsKey(beanName) &&
339+
!this.singletonsCurrentlyInCreation.remove(beanName)) {
312340
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
313341
}
314342
}
315343

316-
public final void setCurrentlyInCreation(String beanName, boolean inCreation) {
317-
if (!inCreation) {
318-
this.inCreationCheckExclusions.add(beanName);
319-
}
320-
else {
321-
this.inCreationCheckExclusions.remove(beanName);
322-
}
323-
}
324-
325-
/**
326-
* Return whether the specified singleton bean is currently in creation
327-
* (within the entire factory).
328-
* @param beanName the name of the bean
329-
*/
330-
public final boolean isSingletonCurrentlyInCreation(String beanName) {
331-
return this.singletonsCurrentlyInCreation.contains(beanName);
332-
}
333-
334344

335345
/**
336346
* Add the given bean to the list of disposable beans in this registry.

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class ConfigurationClassEnhancer {
5656
DisposableBeanMethodInterceptor.class, NoOp.class };
5757

5858
private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
59-
6059
public int accept(Method candidateMethod) {
6160
// Set up the callback filter to return the index of the BeanMethodInterceptor when
6261
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
@@ -72,7 +71,6 @@ public int accept(Method candidateMethod) {
7271

7372
private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor();
7473

75-
7674
private final Callback[] callbackInstances;
7775

7876

@@ -162,6 +160,7 @@ private Class<?> createClass(Enhancer enhancer) {
162160
private static class GetObjectMethodInterceptor implements MethodInterceptor {
163161

164162
private final ConfigurableBeanFactory beanFactory;
163+
165164
private final String beanName;
166165

167166
public GetObjectMethodInterceptor(ConfigurableBeanFactory beanFactory, String beanName) {
@@ -296,7 +295,8 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object
296295
this.beanFactory.setCurrentlyInCreation(beanName, false);
297296
}
298297
return this.beanFactory.getBean(beanName);
299-
} finally {
298+
}
299+
finally {
300300
if (alreadyInCreation) {
301301
this.beanFactory.setCurrentlyInCreation(beanName, true);
302302
}
@@ -347,6 +347,6 @@ private Object enhanceFactoryBean(Class<?> fbClass, String beanName) throws Inst
347347
Enhancer.registerCallbacks(fbSubclass, callbackInstances);
348348
return fbSubclass.newInstance();
349349
}
350-
351350
}
351+
352352
}

0 commit comments

Comments
 (0)