Deprecate convention-based @Component stereotype names in favor of @AliasFor

When use of the deprecated feature is detected, a WARNING log message
will be generated analogous to the following.

WARN o.s.c.a.AnnotationBeanNameGenerator - Support for convention-based
stereotype names is deprecated and will be removed in a future version
of the framework. Please annotate the 'value' attribute in
@org.springframework.context.annotation.AnnotationBeanNameGeneratorTests$ConventionBasedComponent1
with @AliasFor(annotation=Component.class) to declare an explicit alias
for @Component's 'value' attribute.

See gh-31089
Closes gh-31093
This commit is contained in:
Sam Brannen
2023-08-28 15:45:56 +02:00
parent 2d377155ab
commit bfd918d16c
7 changed files with 90 additions and 25 deletions

View File

@@ -22,6 +22,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -77,6 +80,18 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
/**
* Set used to track which stereotype annotations have already been checked
* to see if they use a convention-based override for the {@code value}
* attribute in {@code @Component}.
* @since 6.1
* @see #determineBeanNameFromAnnotation(AnnotatedBeanDefinition)
*/
private static final Set<String> conventionBasedStereotypeCheckCache = ConcurrentHashMap.newKeySet();
private final Log logger = LogFactory.getLog(AnnotationBeanNameGenerator.class);
private final Map<String, Set<String>> metaAnnotationTypesCache = new ConcurrentHashMap<>();
@@ -117,6 +132,15 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
if (isStereotypeWithNameValue(annotationType, metaAnnotationTypes, attributes)) {
Object value = attributes.get("value");
if (value instanceof String currentName && !currentName.isBlank()) {
if (conventionBasedStereotypeCheckCache.add(annotationType) &&
metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) && logger.isWarnEnabled()) {
logger.warn("""
Support for convention-based stereotype names is deprecated and will \
be removed in a future version of the framework. Please annotate the \
'value' attribute in @%s with @AliasFor(annotation=Component.class) \
to declare an explicit alias for @Component's 'value' attribute."""
.formatted(annotationType));
}
if (beanName != null && !currentName.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
"component names: '" + beanName + "' versus '" + currentName + "'");

View File

@@ -23,19 +23,35 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that an annotated class is a "component".
* Such classes are considered as candidates for auto-detection
* Indicates that the annotated class is a <em>component</em>.
*
* <p>Such classes are considered as candidates for auto-detection
* when using annotation-based configuration and classpath scanning.
*
* <p>A component may optionally specify a logical component name via the
* {@link #value value} attribute of this annotation.
*
* <p>Other class-level annotations may be considered as identifying
* a component as well, typically a special kind of component &mdash;
* for example, the {@link Repository @Repository} annotation or AspectJ's
* {@link org.aspectj.lang.annotation.Aspect @Aspect} annotation.
* {@link org.aspectj.lang.annotation.Aspect @Aspect} annotation. Note, however,
* that the {@code @Aspect} annotation does not automatically make a class
* eligible for classpath scanning.
*
* <p>As of Spring Framework 6.1, custom component stereotype annotations should
* use {@link org.springframework.core.annotation.AliasFor @AliasFor} to declare
* an explicit alias for this annotation's {@link #value} attribute. See the
* source code declaration of {@link Repository#value()} and
* <p>Any annotation meta-annotated with {@code @Component} is considered a
* <em>stereotype</em> annotation which makes the annotated class eligible for
* classpath scanning. For example, {@link Service @Service},
* {@link Controller @Controller}, and {@link Repository @Repository} are
* stereotype annotations. Stereotype annotations may also support configuration
* of a logical component name by overriding the {@link #value} attribute of this
* annotation via {@link org.springframework.core.annotation.AliasFor @AliasFor}.
*
* <p>As of Spring Framework 6.1, support for configuring the name of a stereotype
* component by convention (i.e., via a {@code String value()} attribute without
* {@code @AliasFor}) is deprecated and will be removed in a future version of the
* framework. Consequently, custom stereotype annotations must use {@code @AliasFor}
* to declare an explicit alias for this annotation's {@link #value} attribute.
* See the source code declaration of {@link Repository#value()} and
* {@link org.springframework.web.bind.annotation.ControllerAdvice#name()
* ControllerAdvice.name()} for concrete examples.
*