/*
* Copyright 2002-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
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.lang.Nullable;
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
). Candidate classes are detected through configurable type filters. The default filters include classes that are annotated with Spring's @Component
, @Repository
, @Service
, or @Controller
stereotype.
Also supports Java EE 6's ManagedBean
and JSR-330's Named
annotations, if available.
Author: Mark Fisher, Juergen Hoeller, Chris Beams See Also: Since: 2.5
/**
* A bean definition scanner that detects bean candidates on the classpath,
* 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
* {@link org.springframework.stereotype.Component @Component},
* {@link org.springframework.stereotype.Repository @Repository},
* {@link org.springframework.stereotype.Service @Service}, or
* {@link org.springframework.stereotype.Controller @Controller} stereotype.
*
* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
* JSR-330's {@link javax.inject.Named} annotations, if available.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
* @since 2.5
* @see AnnotationConfigApplicationContext#scan
* @see org.springframework.stereotype.Component
* @see org.springframework.stereotype.Repository
* @see org.springframework.stereotype.Service
* @see org.springframework.stereotype.Controller
*/
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
private final BeanDefinitionRegistry registry;
private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();
@Nullable
private String[] autowireCandidatePatterns;
private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
private boolean includeAnnotationConfig = true;
Create a new ClassPathBeanDefinitionScanner
for the given bean factory. Params: - 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 ApplicationContext
implementations.
If given a plain BeanDefinitionRegistry
, the default ResourceLoader
will be a PathMatchingResourcePatternResolver
.
If the passed-in bean factory also implements EnvironmentCapable
its environment will be used by this reader. Otherwise, the reader will initialize and use a StandardEnvironment
. All ApplicationContext
implementations are EnvironmentCapable
, while normal BeanFactory
implementations are not.
Params: - registry – the
BeanFactory
to load bean definitions into, in the form of a BeanDefinitionRegistry
- useDefaultFilters – whether to include the default filters for the
@Component
, @Repository
, @Service
, and @Controller
stereotype annotations
See Also:
/**
* 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 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
* {@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},
* {@link org.springframework.stereotype.Service @Service}, and
* {@link org.springframework.stereotype.Controller @Controller} stereotype annotations
* @see #setResourceLoader
* @see #setEnvironment
*/
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}
Create a new ClassPathBeanDefinitionScanner
for the given bean factory and using the given Environment
when evaluating bean definition profile metadata. 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 ApplicationContext
implementations.
If given a plain BeanDefinitionRegistry
, the default ResourceLoader
will be a PathMatchingResourcePatternResolver
.
Params: - registry – the
BeanFactory
to load bean definitions into, in the form of a BeanDefinitionRegistry
- useDefaultFilters – whether to include the default filters for the
@Component
, @Repository
, @Service
, and @Controller
stereotype annotations - environment – the Spring
Environment
to use when evaluating bean definition profile metadata
See Also: Since: 3.1
/**
* 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
* {@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
* @param environment the Spring {@link Environment} to use when evaluating bean
* definition profile metadata
* @since 3.1
* @see #setResourceLoader
*/
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment) {
this(registry, useDefaultFilters, environment,
(registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}
Create a new ClassPathBeanDefinitionScanner
for the given bean factory and using the given Environment
when evaluating bean definition profile metadata. Params: - registry – the
BeanFactory
to load bean definitions into, in the form of a BeanDefinitionRegistry
- useDefaultFilters – whether to include the default filters for the
@Component
, @Repository
, @Service
, and @Controller
stereotype annotations - environment – the Spring
Environment
to use when evaluating bean definition profile metadata - resourceLoader – the
ResourceLoader
to use
Since: 4.3.6
/**
* Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and
* using the given {@link Environment} when evaluating bean definition profile metadata.
* @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},
* {@link org.springframework.stereotype.Service @Service}, and
* {@link org.springframework.stereotype.Controller @Controller} stereotype annotations
* @param environment the Spring {@link Environment} to use when evaluating bean
* definition profile metadata
* @param resourceLoader the {@link ResourceLoader} to use
* @since 4.3.6
*/
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
Return the BeanDefinitionRegistry that this scanner operates on.
/**
* Return the BeanDefinitionRegistry that this scanner operates on.
*/
@Override
public final BeanDefinitionRegistry getRegistry() {
return this.registry;
}
Set the defaults to use for detected beans.
See Also: - BeanDefinitionDefaults
/**
* Set the defaults to use for detected beans.
* @see BeanDefinitionDefaults
*/
public void setBeanDefinitionDefaults(@Nullable BeanDefinitionDefaults beanDefinitionDefaults) {
this.beanDefinitionDefaults =
(beanDefinitionDefaults != null ? beanDefinitionDefaults : new BeanDefinitionDefaults());
}
Return the defaults to use for detected beans (never null
). Since: 4.1
/**
* Return the defaults to use for detected beans (never {@code null}).
* @since 4.1
*/
public BeanDefinitionDefaults getBeanDefinitionDefaults() {
return this.beanDefinitionDefaults;
}
Set the name-matching patterns for determining autowire candidates.
Params: - autowireCandidatePatterns – the patterns to match against
/**
* Set the name-matching patterns for determining autowire candidates.
* @param autowireCandidatePatterns the patterns to match against
*/
public void setAutowireCandidatePatterns(@Nullable String... autowireCandidatePatterns) {
this.autowireCandidatePatterns = autowireCandidatePatterns;
}
Set the BeanNameGenerator to use for detected bean classes.
Default is a AnnotationBeanNameGenerator
.
/**
* Set the BeanNameGenerator to use for detected bean classes.
* <p>Default is a {@link AnnotationBeanNameGenerator}.
*/
public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
this.beanNameGenerator =
(beanNameGenerator != null ? beanNameGenerator : AnnotationBeanNameGenerator.INSTANCE);
}
Set the ScopeMetadataResolver to use for detected bean classes.
Note that this will override any custom "scopedProxyMode" setting.
The default is an AnnotationScopeMetadataResolver
.
See Also:
/**
* Set the ScopeMetadataResolver to use for detected bean classes.
* Note that this will override any custom "scopedProxyMode" setting.
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
* @see #setScopedProxyMode
*/
public void setScopeMetadataResolver(@Nullable ScopeMetadataResolver scopeMetadataResolver) {
this.scopeMetadataResolver =
(scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
}
Specify the proxy behavior for non-singleton scoped beans.
Note that this will override any custom "scopeMetadataResolver" setting.
The default is ScopedProxyMode.NO
.
See Also:
/**
* Specify the proxy behavior for non-singleton scoped beans.
* Note that this will override any custom "scopeMetadataResolver" setting.
* <p>The default is {@link ScopedProxyMode#NO}.
* @see #setScopeMetadataResolver
*/
public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(scopedProxyMode);
}
Specify whether to register annotation config post-processors.
The default is to register the post-processors. Turn this off
to be able to ignore the annotations or to process them differently.
/**
* Specify whether to register annotation config post-processors.
* <p>The default is to register the post-processors. Turn this off
* to be able to ignore the annotations or to process them differently.
*/
public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) {
this.includeAnnotationConfig = includeAnnotationConfig;
}
Perform a scan within the specified base packages.
Params: - basePackages – the packages to check for annotated classes
Returns: number of beans registered
/**
* Perform a scan within the specified base packages.
* @param basePackages the packages to check for annotated classes
* @return number of beans registered
*/
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
Perform a scan within the specified base packages,
returning the registered bean definitions.
This method does not register an annotation config processor
but rather leaves this up to the caller.
Params: - basePackages – the packages to check for annotated classes
Returns: set of beans registered if any for tooling registration purposes (never null
)
/**
* Perform a scan within the specified base packages,
* returning the registered bean definitions.
* <p>This method does <i>not</i> register an annotation config processor
* but rather leaves this up to the caller.
* @param basePackages the packages to check for annotated classes
* @return set of beans registered if any for tooling registration purposes (never {@code null})
*/
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
Apply further settings to the given bean definition,
beyond the contents retrieved from scanning the component class.
Params: - beanDefinition – the scanned bean definition
- beanName – the generated bean name for the given bean
/**
* Apply further settings to the given bean definition,
* beyond the contents retrieved from scanning the component class.
* @param beanDefinition the scanned bean definition
* @param beanName the generated bean name for the given bean
*/
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
if (this.autowireCandidatePatterns != null) {
beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
}
}
Register the specified bean with the given registry.
Can be overridden in subclasses, e.g. to adapt the registration
process or to register further bean definitions for each scanned bean.
Params: - definitionHolder – the bean definition plus bean name for the bean
- registry – the BeanDefinitionRegistry to register the bean with
/**
* Register the specified bean with the given registry.
* <p>Can be overridden in subclasses, e.g. to adapt the registration
* process or to register further bean definitions for each scanned bean.
* @param definitionHolder the bean definition plus bean name for the bean
* @param registry the BeanDefinitionRegistry to register the bean with
*/
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
Check the given candidate's bean name, determining whether the corresponding
bean definition needs to be registered or conflicts with an existing definition.
Params: - beanName – the suggested name for the bean
- beanDefinition – the corresponding bean definition
Throws: - ConflictingBeanDefinitionException – if an existing, incompatible
bean definition has been found for the specified name
Returns: true
if the bean can be registered as-is; false
if it should be skipped because there is an existing, compatible bean definition for the specified name
/**
* Check the given candidate's bean name, determining whether the corresponding
* bean definition needs to be registered or conflicts with an existing definition.
* @param beanName the suggested name for the bean
* @param beanDefinition the corresponding bean definition
* @return {@code true} if the bean can be registered as-is;
* {@code false} if it should be skipped because there is an
* existing, compatible bean definition for the specified name
* @throws ConflictingBeanDefinitionException if an existing, incompatible
* bean definition has been found for the specified name
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}
Determine whether the given new bean definition is compatible with
the given existing bean definition.
The default implementation considers them as compatible when the existing
bean definition comes from the same source or from a non-scanning source.
Params: - newDefinition – the new bean definition, originated from scanning
- existingDefinition – the existing bean definition, potentially an
explicitly defined one or a previously generated one from scanning
Returns: whether the definitions are considered as compatible, with the
new definition to be skipped in favor of the existing definition
/**
* Determine whether the given new bean definition is compatible with
* the given existing bean definition.
* <p>The default implementation considers them as compatible when the existing
* bean definition comes from the same source or from a non-scanning source.
* @param newDefinition the new bean definition, originated from scanning
* @param existingDefinition the existing bean definition, potentially an
* explicitly defined one or a previously generated one from scanning
* @return whether the definitions are considered as compatible, with the
* new definition to be skipped in favor of the existing definition
*/
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice
newDefinition.equals(existingDefinition)); // scanned equivalent class twice
}
Get the Environment from the given registry if possible, otherwise return a new
StandardEnvironment.
/**
* 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();
}
}