From f6209cd7af800a74151ce0377b9206246d38691b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 9 Nov 2012 00:54:57 +0100 Subject: [PATCH] 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 --- .../config/ConfigurableBeanFactory.java | 6 +- .../factory/support/AbstractBeanFactory.java | 32 ++++----- .../support/DefaultSingletonBeanRegistry.java | 68 +++++++++++-------- .../ConfigurationClassEnhancer.java | 8 +-- 4 files changed, 62 insertions(+), 52 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java index 4bbc74ebdc..35e44cfeee 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -321,8 +321,8 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException; /** - * Explicitly control in-creation status of the specified bean. For - * container internal use only. + * Explicitly control the current in-creation status of the specified bean. + * For container-internal use only. * @param beanName the name of the bean * @param inCreation whether the bean is currently in creation * @since 3.1 diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 296577b930..eb48008ef1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -897,6 +897,22 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName)); } + @Override + public boolean isActuallyInCreation(String beanName) { + return isSingletonCurrentlyInCreation(beanName) || isPrototypeCurrentlyInCreation(beanName); + } + + /** + * Return whether the specified prototype bean is currently in creation + * (within the current thread). + * @param beanName the name of the bean + */ + protected boolean isPrototypeCurrentlyInCreation(String beanName) { + Object curVal = this.prototypesCurrentlyInCreation.get(); + return (curVal != null && + (curVal.equals(beanName) || (curVal instanceof Set && ((Set) curVal).contains(beanName)))); + } + /** * Callback before prototype creation. *

The default implementation register the prototype as currently in creation. @@ -942,22 +958,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp } } - /** - * Return whether the specified prototype bean is currently in creation - * (within the current thread). - * @param beanName the name of the bean - */ - protected final boolean isPrototypeCurrentlyInCreation(String beanName) { - Object curVal = this.prototypesCurrentlyInCreation.get(); - return (curVal != null && - (curVal.equals(beanName) || (curVal instanceof Set && ((Set) curVal).contains(beanName)))); - } - - public boolean isCurrentlyInCreation(String beanName) { - Assert.notNull(beanName, "Bean name must not be null"); - return isSingletonCurrentlyInCreation(beanName) || isPrototypeCurrentlyInCreation(beanName); - } - public void destroyBean(String beanName, Object beanInstance) { destroyBean(beanName, beanInstance, getMergedLocalBeanDefinition(beanName)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 67b3841eb2..b6d363f3ad 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -16,9 +16,7 @@ package org.springframework.beans.factory.support; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -94,11 +92,11 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements /** Set of registered singletons, containing the bean names in registration order */ private final Set registeredSingletons = new LinkedHashSet(16); - /** Names of beans that are currently in creation */ - private final Set singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet()); + /** Names of beans that are currently in creation (using a ConcurrentHashMap as a Set) */ + private final Map singletonsCurrentlyInCreation = new ConcurrentHashMap(); - /** Names of beans currently excluded from in creation checks */ - private final Set inCreationCheckExclusions = new HashSet(); + /** Names of beans currently excluded from in creation checks (using a ConcurrentHashMap as a Set) */ + private final Map inCreationCheckExclusions = new ConcurrentHashMap(); /** List of suppressed Exceptions, available for associating related causes */ private Set suppressedExceptions; @@ -166,7 +164,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements } public Object getSingleton(String beanName) { - return getSingleton(beanName, isSingletonCurrentlyInCreation(beanName)); + return getSingleton(beanName, true); } /** @@ -179,10 +177,10 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements */ protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); - if (singletonObject == null && allowEarlyReference) { + if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); - if (singletonObject == null) { + if (singletonObject == null && allowEarlyReference) { ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); @@ -289,6 +287,34 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements } + public void setCurrentlyInCreation(String beanName, boolean inCreation) { + Assert.notNull(beanName, "Bean name must not be null"); + if (!inCreation) { + this.inCreationCheckExclusions.put(beanName, Boolean.TRUE); + } + else { + this.inCreationCheckExclusions.remove(beanName); + } + } + + public boolean isCurrentlyInCreation(String beanName) { + Assert.notNull(beanName, "Bean name must not be null"); + return (!this.inCreationCheckExclusions.containsKey(beanName) && isActuallyInCreation(beanName)); + } + + protected boolean isActuallyInCreation(String beanName) { + return isSingletonCurrentlyInCreation(beanName); + } + + /** + * Return whether the specified singleton bean is currently in creation + * (within the entire factory). + * @param beanName the name of the bean + */ + public boolean isSingletonCurrentlyInCreation(String beanName) { + return this.singletonsCurrentlyInCreation.containsKey(beanName); + } + /** * Callback before singleton creation. *

Default implementation register the singleton as currently in creation. @@ -296,7 +322,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements * @see #isSingletonCurrentlyInCreation */ protected void beforeSingletonCreation(String beanName) { - if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { + if (!this.inCreationCheckExclusions.containsKey(beanName) && + this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) { throw new BeanCurrentlyInCreationException(beanName); } } @@ -308,29 +335,12 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements * @see #isSingletonCurrentlyInCreation */ protected void afterSingletonCreation(String beanName) { - if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { + if (!this.inCreationCheckExclusions.containsKey(beanName) && + !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } } - public final void setCurrentlyInCreation(String beanName, boolean inCreation) { - if (!inCreation) { - this.inCreationCheckExclusions.add(beanName); - } - else { - this.inCreationCheckExclusions.remove(beanName); - } - } - - /** - * Return whether the specified singleton bean is currently in creation - * (within the entire factory). - * @param beanName the name of the bean - */ - public final boolean isSingletonCurrentlyInCreation(String beanName) { - return this.singletonsCurrentlyInCreation.contains(beanName); - } - /** * Add the given bean to the list of disposable beans in this registry. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 4f569067b8..99678159d3 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -56,7 +56,6 @@ class ConfigurationClassEnhancer { DisposableBeanMethodInterceptor.class, NoOp.class }; private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() { - public int accept(Method candidateMethod) { // Set up the callback filter to return the index of the BeanMethodInterceptor when // handling a @Bean-annotated method; otherwise, return index of the NoOp callback. @@ -72,7 +71,6 @@ class ConfigurationClassEnhancer { private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor(); - private final Callback[] callbackInstances; @@ -162,6 +160,7 @@ class ConfigurationClassEnhancer { private static class GetObjectMethodInterceptor implements MethodInterceptor { private final ConfigurableBeanFactory beanFactory; + private final String beanName; public GetObjectMethodInterceptor(ConfigurableBeanFactory beanFactory, String beanName) { @@ -296,7 +295,8 @@ class ConfigurationClassEnhancer { this.beanFactory.setCurrentlyInCreation(beanName, false); } return this.beanFactory.getBean(beanName); - } finally { + } + finally { if (alreadyInCreation) { this.beanFactory.setCurrentlyInCreation(beanName, true); } @@ -347,6 +347,6 @@ class ConfigurationClassEnhancer { Enhancer.registerCallbacks(fbSubclass, callbackInstances); return fbSubclass.newInstance(); } - } + }