Cache by-type lookups in DefaultListableBeanFactory
Prior to this change, by-type lookups using DLBF#getBeanNamesForType required traversal of all bean definitions within the bean factory in order to inspect their bean class for assignability to the target type. These operations are comparatively expensive and when there are a large number of beans registered within the container coupled with a large number of by-type lookups at runtime, the performance impact can be severe. The test introduced here demonstrates such a scenario clearly. This performance problem is likely to manifest in large Spring-based applications using non-singleton beans, particularly request-scoped beans that may be created and wired many thousands of times per second. This commit introduces a simple ConcurrentHashMap-based caching strategy for by-type lookups; container-wide assignability checks happen only once on the first by-type lookup and are afterwards cached by type with the values in the map being an array of all bean names assignable to that type. This means that at runtime when creating and autowiring non-singleton beans, the cost of by-type lookups is reduced to that of ConcurrentHashMap#get. Issue: SPR-6870
This commit is contained in:
@@ -92,6 +92,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 16 April 2001
|
||||
* @see StaticListableBeanFactory
|
||||
* @see PropertiesBeanDefinitionReader
|
||||
@@ -135,6 +136,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/** Map of bean definition objects, keyed by bean name */
|
||||
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
|
||||
|
||||
/** Map of singleton bean names keyed by bean class */
|
||||
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();
|
||||
|
||||
/** Map of non-singleton bean names keyed by bean class */
|
||||
private final Map<Class<?>, String[]> nonSingletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();
|
||||
|
||||
/** List of bean definition names, in registration order */
|
||||
private final List<String> beanDefinitionNames = new ArrayList<String>();
|
||||
|
||||
@@ -301,6 +308,21 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
if (type == null || !allowEagerInit) {
|
||||
return this.doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
}
|
||||
Map<Class<?>, String[]> cache = includeNonSingletons ?
|
||||
this.nonSingletonBeanNamesByType : this.singletonBeanNamesByType;
|
||||
String[] resolvedBeanNames = cache.get(type);
|
||||
if (resolvedBeanNames != null) {
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
resolvedBeanNames = this.doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
cache.put(type, resolvedBeanNames);
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
|
||||
private String[] doGetBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
// Check all bean definitions.
|
||||
@@ -671,6 +693,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
destroySingleton(beanName);
|
||||
}
|
||||
|
||||
// Remove any assumptions about by-type mappings
|
||||
this.singletonBeanNamesByType.clear();
|
||||
this.nonSingletonBeanNamesByType.clear();
|
||||
|
||||
// Reset all bean definitions that have the given bean as parent (recursively).
|
||||
for (String bdName : this.beanDefinitionNames) {
|
||||
if (!beanName.equals(bdName)) {
|
||||
|
||||
Reference in New Issue
Block a user