From 91f05c8b9d4e108e744484d808564a95b618e731 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Sat, 26 Nov 2011 05:20:29 +0000 Subject: [PATCH] Avoid creation of unnecessary Environment objects Prior to this change, any instantiation of an AnnotationConfigApplicationContext would trigger the creation of three StandardEnvironment objects: one for the ApplicationContext, one for the AnnotatedBeanDefinitionReader, and one for the ClassPathBeanDefinitionScanner. The latter two are immediately swapped out when the ApplicationContext delegates its environment to these subordinate objects anyway. Not only is it inefficient to create these two extra Environments, it creates confusion when debug-level logging is turned on. From the user's perspective and in practice, there is only one Environment; logging should reflect that. This change ensures that only one Environment object is ever created for a given ApplicationContext. If an AnnotatedBeanDefinitionReader or ClassPathBeanDefinitionScanner are used in isolation, e.g. against a plain BeanFactory/BeanDefinitionRegistry, then they will still create their own local StandardEnvironment object. All public API compatibility is preserved; new constructors have been added where necessary to accommodate passing an Environment directly to ABDR ar CPBDS. --- .../AnnotatedBeanDefinitionReader.java | 44 ++++++++-- .../AnnotationConfigApplicationContext.java | 11 +-- .../ClassPathBeanDefinitionScanner.java | 81 ++++++++++++++----- ...athScanningCandidateComponentProvider.java | 7 +- 4 files changed, 106 insertions(+), 37 deletions(-) diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java index 377dc272e8d..3986803b157 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java @@ -45,25 +45,41 @@ public class AnnotatedBeanDefinitionReader { private final BeanDefinitionRegistry registry; - private Environment environment = new StandardEnvironment(); + private Environment environment; private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); /** - * Create a new {@code AnnotatedBeanDefinitionReader} for the given bean factory. + * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry. + * If the registry is {@link EnvironmentCapable}, e.g. is an {@code ApplicationContext}, + * the {@link Environment} will be inherited, otherwise a new + * {@link StandardEnvironment} will be created and used. * @param registry the {@code BeanFactory} to load bean definitions into, * in the form of a {@code BeanDefinitionRegistry} + * @see #AnnotatedBeanDefinitionReader(BeanDefinitionRegistry, Environment) + * @see #setEnvironment(Environment) */ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { + this(registry, getOrCreateEnvironment(registry)); + } + + /** + * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry and using + * the given {@link Environment}. + * @param registry the {@code BeanFactory} to load bean definitions into, + * in the form of a {@code BeanDefinitionRegistry} + * @param environment the {@code Environment} to use when evaluating bean definition + * profiles. + * @since 3.1 + */ + public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); - this.registry = registry; + Assert.notNull(environment, "Environment must not be null"); - // Inherit Environment if possible - if (this.registry instanceof EnvironmentCapable) { - this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); - } + this.registry = registry; + this.environment = environment; AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } @@ -144,4 +160,18 @@ public class AnnotatedBeanDefinitionReader { definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); } + + + /** + * Get the Environment from the given registry if possible, otherwise return a new + * StandardEnvironment. + */ + private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) { + Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); + if (registry instanceof EnvironmentCapable) { + return ((EnvironmentCapable) registry).getEnvironment(); + } + return new StandardEnvironment(); + } + } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java index 13d46584c52..914b9d9ce79 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java @@ -47,9 +47,9 @@ import org.springframework.util.Assert; */ public class AnnotationConfigApplicationContext extends GenericApplicationContext { - private final AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(this); + private final AnnotatedBeanDefinitionReader reader; - private final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this); + private final ClassPathBeanDefinitionScanner scanner; /** @@ -57,7 +57,8 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex * through {@link #register} calls and then manually {@linkplain #refresh refreshed}. */ public AnnotationConfigApplicationContext() { - this.delegateEnvironment(super.getEnvironment()); + this.reader = new AnnotatedBeanDefinitionReader(this); + this.scanner = new ClassPathBeanDefinitionScanner(this); } /** @@ -92,10 +93,6 @@ public class AnnotationConfigApplicationContext extends GenericApplicationContex @Override public void setEnvironment(ConfigurableEnvironment environment) { super.setEnvironment(environment); - delegateEnvironment(environment); - } - - private void delegateEnvironment(ConfigurableEnvironment environment) { this.reader.setEnvironment(environment); this.scanner.setEnvironment(environment); } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java index c931d6ee1a3..d9abfacd5ad 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -27,15 +27,17 @@ import org.springframework.beans.factory.support.BeanDefinitionDefaults; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; +import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ResourceLoader; import org.springframework.util.Assert; import org.springframework.util.PatternMatchUtils; /** * A bean definition scanner that detects bean candidates on the classpath, - * registering corresponding bean definitions with a given registry (BeanFactory - * or ApplicationContext). + * registering corresponding bean definitions with a given registry ({@code BeanFactory} + * or {@code ApplicationContext}). * *

Candidate classes are detected through configurable type filters. The * default filters include classes that are annotated with Spring's @@ -73,29 +75,30 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo /** - * Create a new ClassPathBeanDefinitionScanner for the given bean factory. - * @param registry the BeanFactory to load bean definitions into, - * in the form of a BeanDefinitionRegistry + * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory. + * @param registry the {@code BeanFactory} to load bean definitions into, in the form + * of a {@code BeanDefinitionRegistry} */ public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this(registry, true); } /** - * Create a new ClassPathBeanDefinitionScanner for the given bean factory. - *

If the passed-in bean factory does not only implement the BeanDefinitionRegistry - * interface but also the ResourceLoader interface, it will be used as default - * ResourceLoader as well. This will usually be the case for - * {@link org.springframework.context.ApplicationContext} implementations. - *

If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a - * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}. + * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory. + *

If the passed-in bean factory does not only implement the + * {@code BeanDefinitionRegistry} interface but also the {@code ResourceLoader} + * interface, it will be used as default {@code ResourceLoader} as well. This will + * usually be the case for {@link org.springframework.context.ApplicationContext} + * implementations. + *

If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader} + * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}. *

If the the passed-in bean factory also implements {@link EnvironmentCapable} its * environment will be used by this reader. Otherwise, the reader will initialize and * use a {@link org.springframework.core.env.StandardEnvironment}. All - * ApplicationContext implementations are EnvironmentCapable, while normal BeanFactory - * implementations are not. - * @param registry the BeanFactory to load bean definitions into, - * in the form of a BeanDefinitionRegistry + * {@code ApplicationContext} implementations are {@code EnvironmentCapable}, while + * normal {@code BeanFactory} implementations are not. + * @param registry the {@code BeanFactory} to load bean definitions into, in the form + * of a {@code BeanDefinitionRegistry} * @param useDefaultFilters whether to include the default filters for the * {@link org.springframework.stereotype.Component @Component}, * {@link org.springframework.stereotype.Repository @Repository}, @@ -106,7 +109,33 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo * @see #setEnvironment */ public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { - super(useDefaultFilters); + this(registry, useDefaultFilters, getOrCreateEnvironment(registry)); + } + + /** + * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and + * using the given {@link Environment} when evaluating bean definition profile metadata. + *

If the passed-in bean factory does not only implement the {@code + * BeanDefinitionRegistry} interface but also the {@link ResourceLoader} interface, it + * will be used as default {@code ResourceLoader} as well. This will usually be the + * case for {@link org.springframework.context.ApplicationContext} implementations. + *

If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader} + * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}. + * @param registry the {@code BeanFactory} to load bean definitions into, in the form + * of a {@code BeanDefinitionRegistry} + * @param useDefaultFilters whether to include the default filters for the + * @param environment the Spring {@link Environment} to use when evaluating bean + * definition profile metadata. + * {@link org.springframework.stereotype.Component @Component}, + * {@link org.springframework.stereotype.Repository @Repository}, + * {@link org.springframework.stereotype.Service @Service}, and + * {@link org.springframework.stereotype.Controller @Controller} stereotype + * annotations. + * @since 3.1 + * @see #setResourceLoader + */ + public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) { + super(useDefaultFilters, environment); Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; @@ -115,11 +144,6 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo if (this.registry instanceof ResourceLoader) { setResourceLoader((ResourceLoader) this.registry); } - - // Inherit Environment if possible - if (this.registry instanceof EnvironmentCapable) { - setEnvironment(((EnvironmentCapable) this.registry).getEnvironment()); - } } @@ -307,4 +331,17 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo newDefinition.equals(existingDefinition)); // scanned equivalent class twice } + + /** + * Get the Environment from the given registry if possible, otherwise return a new + * StandardEnvironment. + */ + private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) { + Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); + if (registry instanceof EnvironmentCapable) { + return ((EnvironmentCapable) registry).getEnvironment(); + } + return new StandardEnvironment(); + } + } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java index d077ac89b2b..877ba1bddad 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -73,7 +73,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC protected final Log logger = LogFactory.getLog(getClass()); - private Environment environment = new StandardEnvironment(); + private Environment environment; private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); @@ -95,9 +95,14 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC * @see #registerDefaultFilters() */ public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) { + this(useDefaultFilters, new StandardEnvironment()); + } + + public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) { if (useDefaultFilters) { registerDefaultFilters(); } + this.environment = environment; }