Merge branch '6.2.x'
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -34,6 +35,7 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotation.Adapt;
|
||||
@@ -41,6 +43,7 @@ import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -143,16 +146,26 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
|
||||
Set<String> metaAnnotationTypes = this.metaAnnotationTypesCache.computeIfAbsent(annotationType,
|
||||
key -> getMetaAnnotationTypes(mergedAnnotation));
|
||||
if (isStereotypeWithNameValue(annotationType, metaAnnotationTypes, attributes)) {
|
||||
Object value = attributes.get("value");
|
||||
Object value = attributes.get(MergedAnnotation.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 (hasExplicitlyAliasedValueAttribute(mergedAnnotation.getType())) {
|
||||
logger.warn("""
|
||||
Although the 'value' attribute in @%s declares @AliasFor for an attribute \
|
||||
other than @Component's 'value' attribute, the value is still used as the \
|
||||
@Component name based on convention. As of Spring Framework 7.0, such a \
|
||||
'value' attribute will no longer be used as the @Component name."""
|
||||
.formatted(annotationType));
|
||||
}
|
||||
else {
|
||||
logger.warn("""
|
||||
Support for convention-based @Component 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 " +
|
||||
@@ -216,7 +229,7 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
|
||||
boolean isStereotype = metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
|
||||
annotationType.equals("jakarta.inject.Named");
|
||||
|
||||
return (isStereotype && attributes.containsKey("value"));
|
||||
return (isStereotype && attributes.containsKey(MergedAnnotation.VALUE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,4 +260,14 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
|
||||
return StringUtils.uncapitalizeAsProperty(shortClassName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied annotation type declares a {@code value()} attribute
|
||||
* with an explicit alias configured via {@link AliasFor @AliasFor}.
|
||||
* @since 6.2.3
|
||||
*/
|
||||
private static boolean hasExplicitlyAliasedValueAttribute(Class<? extends Annotation> annotationType) {
|
||||
Method valueAttribute = ReflectionUtils.findMethod(annotationType, MergedAnnotation.VALUE);
|
||||
return (valueAttribute != null && valueAttribute.isAnnotationPresent(AliasFor.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -150,6 +150,25 @@ class AnnotationBeanNameGeneratorTests {
|
||||
assertGeneratedName(RestControllerAdviceClass.class, "myRestControllerAdvice");
|
||||
}
|
||||
|
||||
@Test // gh-34317
|
||||
void generateBeanNameFromStereotypeAnnotationWithStringValueAsExplicitAliasForMetaAnnotationOtherThanComponent() {
|
||||
// As of Spring Framework 6.2, "enigma" is incorrectly used as the @Component name.
|
||||
// As of Spring Framework 7.0, the generated name will be "annotationBeanNameGeneratorTests.StereotypeWithoutExplicitName".
|
||||
assertGeneratedName(StereotypeWithoutExplicitName.class, "enigma");
|
||||
}
|
||||
|
||||
@Test // gh-34317
|
||||
void generateBeanNameFromStereotypeAnnotationWithStringValueAndExplicitAliasForComponentNameWithBlankName() {
|
||||
// As of Spring Framework 6.2, "enigma" is incorrectly used as the @Component name.
|
||||
// As of Spring Framework 7.0, the generated name will be "annotationBeanNameGeneratorTests.StereotypeWithGeneratedName".
|
||||
assertGeneratedName(StereotypeWithGeneratedName.class, "enigma");
|
||||
}
|
||||
|
||||
@Test // gh-34317
|
||||
void generateBeanNameFromStereotypeAnnotationWithStringValueAndExplicitAliasForComponentName() {
|
||||
assertGeneratedName(StereotypeWithExplicitName.class, "explicitName");
|
||||
}
|
||||
|
||||
|
||||
private void assertGeneratedName(Class<?> clazz, String expectedName) {
|
||||
BeanDefinition bd = annotatedBeanDef(clazz);
|
||||
@@ -192,7 +211,7 @@ class AnnotationBeanNameGeneratorTests {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Component
|
||||
@interface ConventionBasedComponent1 {
|
||||
// This intentionally convention-based. Please do not add @AliasFor.
|
||||
// This is intentionally convention-based. Please do not add @AliasFor.
|
||||
// See gh-31093.
|
||||
String value() default "";
|
||||
}
|
||||
@@ -200,7 +219,7 @@ class AnnotationBeanNameGeneratorTests {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Component
|
||||
@interface ConventionBasedComponent2 {
|
||||
// This intentionally convention-based. Please do not add @AliasFor.
|
||||
// This is intentionally convention-based. Please do not add @AliasFor.
|
||||
// See gh-31093.
|
||||
String value() default "";
|
||||
}
|
||||
@@ -242,7 +261,7 @@ class AnnotationBeanNameGeneratorTests {
|
||||
@Target(ElementType.TYPE)
|
||||
@Controller
|
||||
@interface TestRestController {
|
||||
// This intentionally convention-based. Please do not add @AliasFor.
|
||||
// This is intentionally convention-based. Please do not add @AliasFor.
|
||||
// See gh-31093.
|
||||
String value() default "";
|
||||
}
|
||||
@@ -301,7 +320,6 @@ class AnnotationBeanNameGeneratorTests {
|
||||
String[] basePackages() default {};
|
||||
}
|
||||
|
||||
|
||||
@TestControllerAdvice(basePackages = "com.example", name = "myControllerAdvice")
|
||||
static class ControllerAdviceClass {
|
||||
}
|
||||
@@ -310,4 +328,56 @@ class AnnotationBeanNameGeneratorTests {
|
||||
static class RestControllerAdviceClass {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@interface MetaAnnotationWithStringAttribute {
|
||||
|
||||
String attribute() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom stereotype annotation which has a {@code String value} attribute that
|
||||
* is explicitly declared as an alias for an attribute in a meta-annotation
|
||||
* other than {@link Component @Component}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Component
|
||||
@MetaAnnotationWithStringAttribute
|
||||
@interface MyStereotype {
|
||||
|
||||
@AliasFor(annotation = MetaAnnotationWithStringAttribute.class, attribute = "attribute")
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@MyStereotype("enigma")
|
||||
static class StereotypeWithoutExplicitName {
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom stereotype annotation which is identical to {@link MyStereotype @MyStereotype}
|
||||
* except that it has a {@link #name} attribute that is an explicit alias for
|
||||
* {@link Component#value}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Component
|
||||
@MetaAnnotationWithStringAttribute
|
||||
@interface MyNamedStereotype {
|
||||
|
||||
@AliasFor(annotation = MetaAnnotationWithStringAttribute.class, attribute = "attribute")
|
||||
String value() default "";
|
||||
|
||||
@AliasFor(annotation = Component.class, attribute = "value")
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@MyNamedStereotype(value = "enigma", name ="explicitName")
|
||||
static class StereotypeWithExplicitName {
|
||||
}
|
||||
|
||||
@MyNamedStereotype(value = "enigma")
|
||||
static class StereotypeWithGeneratedName {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user