From 965bea7b3e4e1a0af44acb206e71c63ca37183a6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 1 Nov 2014 10:11:20 +0100 Subject: [PATCH] DefaultListableBeanFactory efficiently accesses current bean names and exposes them via getBeanNamesIterator() Issue: SPR-12404 --- .../ConfigurableListableBeanFactory.java | 18 +- .../support/DefaultListableBeanFactory.java | 177 +++++++++++------- .../DefaultListableBeanFactoryTests.java | 22 ++- ...=> DefaultSingletonBeanRegistryTests.java} | 18 +- 4 files changed, 153 insertions(+), 82 deletions(-) rename spring-beans/src/test/java/org/springframework/beans/factory/{SharedBeanRegistryTests.java => DefaultSingletonBeanRegistryTests.java} (83%) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java index 2dc8ec6fae..0c5ee75fed 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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. @@ -16,6 +16,8 @@ package org.springframework.beans.factory.config; +import java.util.Iterator; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -105,6 +107,20 @@ public interface ConfigurableListableBeanFactory */ BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; + /** + * Return a unified view over all bean names managed by this factory. + *

Includes bean definition names as well as names of manually registered + * singleton instances, with bean definition names consistently coming first, + * analogous to how type/annotation specific retrieval of bean names works. + * @return the composite iterator for the bean names view + * @since 4.1.2 + * @see #containsBeanDefinition + * @see #registerSingleton + * @see #getBeanNamesForType + * @see #getBeanNamesForAnnotation + */ + Iterator getBeanNamesIterator(); + /** * Freeze all bean definitions, signalling that the registered bean definitions * will not be modified or post-processed any further. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 843ae4b80a..93e646070f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -34,7 +34,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -68,6 +70,7 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.UsesJava8; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.CompositeIterator; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -156,14 +159,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** Map of bean definition objects, keyed by bean name */ private final Map beanDefinitionMap = new ConcurrentHashMap(64); - /** Map of singleton and non-singleton bean names keyed by dependency type */ + /** Map of singleton and non-singleton bean names, keyed by dependency type */ private final Map, String[]> allBeanNamesByType = new ConcurrentHashMap, String[]>(64); - /** Map of singleton-only bean names keyed by dependency type */ + /** Map of singleton-only bean names, keyed by dependency type */ private final Map, String[]> singletonBeanNamesByType = new ConcurrentHashMap, String[]>(64); /** List of bean definition names, in registration order */ - private final List beanDefinitionNames = new ArrayList(); + private final List beanDefinitionNames = new ArrayList(64); + + /** List of names of manually registered singletons, in registration order */ + private final Set manualSingletonNames = new LinkedHashSet(16); /** Whether bean definition metadata may be cached for all beans */ private boolean configurationFrozen = false; @@ -202,6 +208,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto this.serializationId = serializationId; } + /** + * Return an id for serialization purposes, if specified, allowing this BeanFactory + * to be deserialized from this id back into the BeanFactory object, if needed. + * @since 4.1.2 + */ + public String getSerializationId() { + return this.serializationId; + } + /** * Set whether it should be allowed to override bean definitions by registering * a different definition with the same name, automatically replacing the former. @@ -213,6 +228,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding; } + /** + * Return whether it should be allowed to override bean definitions by registering + * a different definition with the same name, automatically replacing the former. + * @since 4.1.2 + */ + public boolean isAllowBeanDefinitionOverriding() { + return this.allowBeanDefinitionOverriding; + } + /** * Set whether the factory is allowed to eagerly load bean classes * even for bean definitions that are marked as "lazy-init". @@ -227,6 +251,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto this.allowEagerClassLoading = allowEagerClassLoading; } + /** + * Return whether the factory is allowed to eagerly load bean classes + * even for bean definitions that are marked as "lazy-init". + * @since 4.1.2 + */ + public boolean isAllowEagerClassLoading() { + return this.allowEagerClassLoading; + } + /** * Set a {@link java.util.Comparator} for dependency Lists and arrays. * @see org.springframework.core.OrderComparator @@ -352,13 +385,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override public String[] getBeanDefinitionNames() { - synchronized (this.beanDefinitionMap) { - if (this.frozenBeanDefinitionNames != null) { - return this.frozenBeanDefinitionNames; - } - else { - return StringUtils.toStringArray(this.beanDefinitionNames); - } + if (this.frozenBeanDefinitionNames != null) { + return this.frozenBeanDefinitionNames; + } + else { + return StringUtils.toStringArray(this.beanDefinitionNames); } } @@ -389,8 +420,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto List result = new ArrayList(); // Check all bean definitions. - String[] beanDefinitionNames = getBeanDefinitionNames(); - for (String beanName : beanDefinitionNames) { + for (String beanName : this.beanDefinitionNames) { // Only consider bean as eligible if the bean name // is not defined as alias for some other bean. if (!isAlias(beanName)) { @@ -398,7 +428,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Only check bean definition if it is complete. if (!mbd.isAbstract() && (allowEagerInit || - ((mbd.hasBeanClass() || !mbd.isLazyInit() || this.allowEagerClassLoading)) && + ((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) && !requiresEagerInitForType(mbd.getFactoryBeanName()))) { // In case of FactoryBean, match object created by FactoryBean. boolean isFactoryBean = isFactoryBean(beanName, mbd); @@ -437,9 +467,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } } - // Check singletons too, to catch manually registered singletons. - String[] singletonNames = getSingletonNames(); - for (String beanName : singletonNames) { + // Check manually registered singletons too. + for (String beanName : this.manualSingletonNames) { // Only check if manually registered. if (!containsBeanDefinition(beanName)) { // In case of FactoryBean, match object created by FactoryBean. @@ -512,13 +541,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override public String[] getBeanNamesForAnnotation(Class annotationType) { List results = new ArrayList(); - for (String beanName : getBeanDefinitionNames()) { + for (String beanName : this.beanDefinitionNames) { BeanDefinition beanDefinition = getBeanDefinition(beanName); if (!beanDefinition.isAbstract() && findAnnotationOnBean(beanName, annotationType) != null) { results.add(beanName); } } - for (String beanName : getSingletonNames()) { + for (String beanName : this.manualSingletonNames) { if (!results.contains(beanName) && findAnnotationOnBean(beanName, annotationType) != null) { results.add(beanName); } @@ -529,13 +558,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override public Map getBeansWithAnnotation(Class annotationType) { Map results = new LinkedHashMap(); - for (String beanName : getBeanDefinitionNames()) { + for (String beanName : this.beanDefinitionNames) { BeanDefinition beanDefinition = getBeanDefinition(beanName); if (!beanDefinition.isAbstract() && findAnnotationOnBean(beanName, annotationType) != null) { results.put(beanName, getBean(beanName)); } } - for (String beanName : getSingletonNames()) { + for (String beanName : this.manualSingletonNames) { if (!results.containsKey(beanName) && findAnnotationOnBean(beanName, annotationType) != null) { results.put(beanName, getBean(beanName)); } @@ -662,12 +691,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return bd; } + @Override + public Iterator getBeanNamesIterator() { + CompositeIterator iterator = new CompositeIterator(); + iterator.add(this.beanDefinitionNames.iterator()); + iterator.add(this.manualSingletonNames.iterator()); + return iterator; + } + @Override public void freezeConfiguration() { this.configurationFrozen = true; - synchronized (this.beanDefinitionMap) { - this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames); - } + this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames); } @Override @@ -691,12 +726,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto this.logger.debug("Pre-instantiating singletons in " + this); } - List beanNames; - synchronized (this.beanDefinitionMap) { - // Iterate over a copy to allow for init methods which in turn register new bean definitions. - // While this may not be part of the regular factory bootstrap, it does otherwise work fine. - beanNames = new ArrayList(this.beanDefinitionNames); - } + // Iterate over a copy to allow for init methods which in turn register new bean definitions. + // While this may not be part of the regular factory bootstrap, it does otherwise work fine. + List beanNames = new ArrayList(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { @@ -772,35 +804,34 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto BeanDefinition oldBeanDefinition; - synchronized (this.beanDefinitionMap) { - oldBeanDefinition = this.beanDefinitionMap.get(beanName); - if (oldBeanDefinition != null) { - if (!this.allowBeanDefinitionOverriding) { - throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, - "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + - "': There is already [" + oldBeanDefinition + "] bound."); - } - else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { - // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE - if (this.logger.isWarnEnabled()) { - this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + - " with a framework-generated bean definition ': replacing [" + - oldBeanDefinition + "] with [" + beanDefinition + "]"); - } - } - else { - if (this.logger.isInfoEnabled()) { - this.logger.info("Overriding bean definition for bean '" + beanName + - "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); - } + oldBeanDefinition = this.beanDefinitionMap.get(beanName); + if (oldBeanDefinition != null) { + if (!this.allowBeanDefinitionOverriding) { + throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, + "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + + "': There is already [" + oldBeanDefinition + "] bound."); + } + else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { + // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE + if (this.logger.isWarnEnabled()) { + this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + + " with a framework-generated bean definition ': replacing [" + + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { - this.beanDefinitionNames.add(beanName); - this.frozenBeanDefinitionNames = null; + if (this.logger.isInfoEnabled()) { + this.logger.info("Overriding bean definition for bean '" + beanName + + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); + } } - this.beanDefinitionMap.put(beanName, beanDefinition); } + else { + this.beanDefinitionNames.add(beanName); + this.manualSingletonNames.remove(beanName); + this.frozenBeanDefinitionNames = null; + } + this.beanDefinitionMap.put(beanName, beanDefinition); if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); @@ -811,17 +842,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { Assert.hasText(beanName, "'beanName' must not be empty"); - synchronized (this.beanDefinitionMap) { - BeanDefinition bd = this.beanDefinitionMap.remove(beanName); - if (bd == null) { - if (this.logger.isTraceEnabled()) { - this.logger.trace("No bean named '" + beanName + "' found in " + this); - } - throw new NoSuchBeanDefinitionException(beanName); + BeanDefinition bd = this.beanDefinitionMap.remove(beanName); + if (bd == null) { + if (this.logger.isTraceEnabled()) { + this.logger.trace("No bean named '" + beanName + "' found in " + this); } - this.beanDefinitionNames.remove(beanName); - this.frozenBeanDefinitionNames = null; + throw new NoSuchBeanDefinitionException(beanName); } + this.beanDefinitionNames.remove(beanName); + this.frozenBeanDefinitionNames = null; resetBeanDefinition(beanName); } @@ -856,18 +885,22 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto */ @Override protected boolean allowAliasOverriding() { - return this.allowBeanDefinitionOverriding; + return isAllowBeanDefinitionOverriding(); } @Override public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException { super.registerSingleton(beanName, singletonObject); + if (!this.beanDefinitionMap.containsKey(beanName)) { + this.manualSingletonNames.add(beanName); + } clearByTypeCache(); } @Override public void destroySingleton(String beanName) { super.destroySingleton(beanName); + this.manualSingletonNames.remove(beanName); clearByTypeCache(); } @@ -940,7 +973,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); Object result = converter.convertIfNecessary(matchingBeans.values(), type); - if (this.dependencyComparator != null && result instanceof Object[]) { + if (getDependencyComparator() != null && result instanceof Object[]) { Arrays.sort((Object[]) result, adaptDependencyComparator(matchingBeans)); } return result; @@ -967,7 +1000,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); Object result = converter.convertIfNecessary(matchingBeans.values(), type); - if (this.dependencyComparator != null && result instanceof List) { + if (getDependencyComparator() != null && result instanceof List) { Collections.sort((List) result, adaptDependencyComparator(matchingBeans)); } return result; @@ -1030,12 +1063,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } private Comparator adaptDependencyComparator(Map matchingBeans) { - if (this.dependencyComparator instanceof OrderComparator) { - return ((OrderComparator) this.dependencyComparator).withSourceProvider( + Comparator comparator = getDependencyComparator(); + if (comparator instanceof OrderComparator) { + return ((OrderComparator) comparator).withSourceProvider( createFactoryAwareOrderSourceProvider(matchingBeans)); } else { - return this.dependencyComparator; + return comparator; } } @@ -1223,8 +1257,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto * @return the priority assigned to that bean or {@code null} if none is set */ protected Integer getPriority(Object beanInstance) { - if (this.dependencyComparator instanceof OrderComparator) { - return ((OrderComparator) this.dependencyComparator).getPriority(beanInstance); + Comparator comparator = getDependencyComparator(); + if (comparator instanceof OrderComparator) { + return ((OrderComparator) comparator).getPriority(beanInstance); } return null; } @@ -1266,7 +1301,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto public String toString() { StringBuilder sb = new StringBuilder(ObjectUtils.identityToString(this)); sb.append(": defining beans ["); - sb.append(StringUtils.arrayToCommaDelimitedString(getBeanDefinitionNames())); + sb.append(StringUtils.collectionToCommaDelimitedString(this.beanDefinitionNames)); sb.append("]; "); BeanFactory parent = getParentBeanFactory(); if (parent == null) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index 767961d3bb..d1cec3117e 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1000,6 +1000,16 @@ public class DefaultListableBeanFactoryTests { beansOfType = lbf.getBeansOfType(null, false, true); assertEquals(2, beansOfType.size()); + + Iterator beanNames = lbf.getBeanNamesIterator(); + assertEquals("test", beanNames.next()); + assertEquals("singletonObject", beanNames.next()); + assertFalse(beanNames.hasNext()); + + assertTrue(lbf.containsSingleton("test")); + assertTrue(lbf.containsSingleton("singletonObject")); + assertTrue(lbf.containsBeanDefinition("test")); + assertFalse(lbf.containsBeanDefinition("singletonObject")); } @Test @@ -1010,8 +1020,8 @@ public class DefaultListableBeanFactoryTests { p.setProperty("test.name", "Tony"); p.setProperty("test.age", "48"); p.setProperty("test.spouse(ref)", "singletonObject"); - p.setProperty("singletonObject.(class)", org.springframework.beans.factory.config.PropertiesFactoryBean.class.getName()); (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + lbf.registerBeanDefinition("singletonObject", new RootBeanDefinition(PropertiesFactoryBean.class)); Object singletonObject = new TestBean(); lbf.registerSingleton("singletonObject", singletonObject); lbf.preInstantiateSingletons(); @@ -1028,7 +1038,17 @@ public class DefaultListableBeanFactoryTests { assertTrue(beansOfType.containsValue(singletonObject)); beansOfType = lbf.getBeansOfType(null, false, true); + + Iterator beanNames = lbf.getBeanNamesIterator(); + assertEquals("test", beanNames.next()); + assertEquals("singletonObject", beanNames.next()); + assertFalse(beanNames.hasNext()); assertEquals(2, beansOfType.size()); + + assertTrue(lbf.containsSingleton("test")); + assertTrue(lbf.containsSingleton("singletonObject")); + assertTrue(lbf.containsBeanDefinition("test")); + assertTrue(lbf.containsBeanDefinition("singletonObject")); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/SharedBeanRegistryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultSingletonBeanRegistryTests.java similarity index 83% rename from spring-beans/src/test/java/org/springframework/beans/factory/SharedBeanRegistryTests.java rename to spring-beans/src/test/java/org/springframework/beans/factory/DefaultSingletonBeanRegistryTests.java index 93321b43b4..037afde755 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/SharedBeanRegistryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultSingletonBeanRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -16,8 +16,6 @@ package org.springframework.beans.factory; -import java.util.Arrays; - import org.junit.Test; import org.springframework.beans.BeansException; @@ -32,7 +30,7 @@ import static org.junit.Assert.*; * @author Chris Beams * @since 04.07.2006 */ -public final class SharedBeanRegistryTests { +public class DefaultSingletonBeanRegistryTests { @Test public void testSingletons() { @@ -53,9 +51,10 @@ public final class SharedBeanRegistryTests { assertSame(tb, beanRegistry.getSingleton("tb")); assertSame(tb2, beanRegistry.getSingleton("tb2")); assertEquals(2, beanRegistry.getSingletonCount()); - assertEquals(2, beanRegistry.getSingletonNames().length); - assertTrue(Arrays.asList(beanRegistry.getSingletonNames()).contains("tb")); - assertTrue(Arrays.asList(beanRegistry.getSingletonNames()).contains("tb2")); + String[] names = beanRegistry.getSingletonNames(); + assertEquals(2, names.length); + assertEquals("tb", names[0]); + assertEquals("tb2", names[1]); beanRegistry.destroySingletons(); assertEquals(0, beanRegistry.getSingletonCount()); @@ -73,8 +72,9 @@ public final class SharedBeanRegistryTests { assertSame(tb, beanRegistry.getSingleton("tb")); assertEquals(1, beanRegistry.getSingletonCount()); - assertEquals(1, beanRegistry.getSingletonNames().length); - assertTrue(Arrays.asList(beanRegistry.getSingletonNames()).contains("tb")); + String[] names = beanRegistry.getSingletonNames(); + assertEquals(1, names.length); + assertEquals("tb", names[0]); assertFalse(tb.wasDestroyed()); beanRegistry.destroySingletons();