Introduce BeanNameGenerator based on fully qualified class name

Prior to this commit, Spring offered two top-level implementations of
the BeanNameGenerator strategy: DefaultBeanNameGenerator and
AnnotationBeanNameGenerator. The latter is used as the default bean
name generator for beans picked up via component scanning. In a typical
application, this strategy works well; however, if multiple component
scanned beans have the same simple class name (i.e., identical names
ignoring the package), a BeanDefinitionStoreException is thrown.

To address such naming conflicts, users of Spring have had to implement
a custom BeanNameGenerator based on the fully qualified class name of
such components.

Similar conflicts can arise with components registered via
configuration class imports (i.e., via @Import), and
ConfigurationClassPostProcessor addresses this via an anonymous inner
class that extends AnnotationBeanNameGenerator but falls back to using
the fully qualified class name if an explicit bean name is not provided
via an annotation.

This commit extracts the implementation of
ConfigurationClassPostProcessor's internal BeanNameGenerator into a new
top-level FullyQualifiedAnnotationBeanNameGenerator class that can be
used to disambiguate between same-named components residing in
different packages that are picked up via component scanning. This bean
name generator can be configured via @ComponentScan's nameGenerator
attribute.

Closes gh-24114
This commit is contained in:
Sam Brannen 2020-01-07 19:39:28 +01:00
parent e1fb4a1966
commit b4c91e7dac
3 changed files with 52 additions and 9 deletions

View File

@ -57,6 +57,7 @@ import org.springframework.util.StringUtils;
* @see org.springframework.stereotype.Service#value()
* @see org.springframework.stereotype.Controller#value()
* @see javax.inject.Named#value()
* @see FullyQualifiedAnnotationBeanNameGenerator
*/
public class AnnotationBeanNameGenerator implements BeanNameGenerator {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@ -95,14 +95,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
* @since 5.2
* @see #setBeanNameGenerator
*/
public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR = new AnnotationBeanNameGenerator() {
@Override
protected String buildDefaultBeanName(BeanDefinition definition) {
String beanClassName = definition.getBeanClassName();
Assert.state(beanClassName != null, "No bean class name set");
return beanClassName;
}
};
public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR =
new FullyQualifiedAnnotationBeanNameGenerator();
private static final String IMPORT_REGISTRY_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";

View File

@ -0,0 +1,48 @@
/*
* Copyright 2002-2020 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 org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.util.Assert;
/**
* An extension of {@code AnnotationBeanNameGenerator} that uses the fully qualified
* class name as the default bean name if an explicit bean name is not supplied via
* a supported type-level annotation such as {@code @Component} (see
* {@link AnnotationBeanNameGenerator} for details on supported annotations).
*
* <p>Note that an instance of this class is used by default for configuration-level
* import purposes; whereas, the default for component scanning purposes is a plain
* {@code AnnotationBeanNameGenerator}.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 5.2.3
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
* @see AnnotationBeanNameGenerator
* @see ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR
*/
public class FullyQualifiedAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
@Override
protected String buildDefaultBeanName(BeanDefinition definition) {
String beanClassName = definition.getBeanClassName();
Assert.state(beanClassName != null, "No bean class name set");
return beanClassName;
}
}