提交 91f05c8b 编写于 作者: C Chris Beams

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.
上级 143db0d8
......@@ -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();
}
}
......@@ -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);
}
......
......@@ -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}).
*
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader}
* will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
* <p>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.
* <p>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.
* <p>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();
}
}
......@@ -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;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册