All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.context.annotation.ClassPathBeanDefinitionScanner Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * Copyright 2002-2023 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 ({@code BeanFactory}
 * or {@code ApplicationContext}).
 *
 * 

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. * *

Also supports Jakarta EE's {@link jakarta.annotation.ManagedBean} and * JSR-330's {@link jakarta.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 {@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 {@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 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 {@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 * {@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 ? resourceLoader : null)); } /** * 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. */ @Override public final BeanDefinitionRegistry getRegistry() { return this.registry; } /** * 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 {@code null}). * @since 4.1 */ public BeanDefinitionDefaults getBeanDefinitionDefaults() { return this.beanDefinitionDefaults; } /** * 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 {@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 {@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 {@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. */ public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) { this.includeAnnotationConfig = includeAnnotationConfig; } /** * 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. * @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 doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { Set 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 abstractBeanDefinition) { postProcessBeanDefinition(abstractBeanDefinition, beanName); } if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition); } 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. * @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. * @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. * @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 IllegalStateException 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. * @param newDef the new bean definition, originated from scanning * @param existingDef 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 newDef, BeanDefinition existingDef) { return (!(existingDef instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean (newDef.getSource() != null && newDef.getSource().equals(existingDef.getSource())) || // scanned same file twice newDef.equals(existingDef)); // 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 environmentCapable) { return environmentCapable.getEnvironment(); } return new StandardEnvironment(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy