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:
@@ -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 + "'");
|
||||
|
||||
@@ -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 —
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -32,6 +33,7 @@ public @interface DevComponent {
|
||||
|
||||
String PROFILE_NAME = "dev";
|
||||
|
||||
@AliasFor(annotation = Component.class)
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,7 @@ import org.springframework.stereotype.Service;
|
||||
@Scope("prototype")
|
||||
public @interface CustomStereotype {
|
||||
|
||||
@AliasFor(annotation = Service.class)
|
||||
String value() default "thoreau";
|
||||
|
||||
}
|
||||
|
||||
@@ -210,12 +210,16 @@ class AnnotationBeanNameGeneratorTests {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Component
|
||||
@interface ConventionBasedComponent1 {
|
||||
// This intentionally convention-based. Please do not add @AliasFor.
|
||||
// See gh-31093.
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Component
|
||||
@interface ConventionBasedComponent2 {
|
||||
// This intentionally convention-based. Please do not add @AliasFor.
|
||||
// See gh-31093.
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@@ -256,7 +260,8 @@ class AnnotationBeanNameGeneratorTests {
|
||||
@Target(ElementType.TYPE)
|
||||
@Controller
|
||||
@interface TestRestController {
|
||||
|
||||
// This intentionally convention-based. Please do not add @AliasFor.
|
||||
// See gh-31093.
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -62,9 +63,12 @@ class ConfigurationMetaAnnotationTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Configuration
|
||||
@interface TestConfiguration {
|
||||
|
||||
@AliasFor(annotation = Configuration.class)
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user