From a68f5b1674635bf3de79435f83c3d8f4b7ae9098 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 7 Jul 2022 17:36:16 +0200 Subject: [PATCH] Deprecate convention-based annotation attribute overrides Implicit convention-based annotation attribute overrides have been supported for a long time; however, Spring Framework 4.2 introduced support for explicit annotation attribute overrides via @AliasFor. Since explicit overrides are favorable to implicit overrides, and since the support for convention-based overrides increases the complexity of Spring's annotation search algorithms, this commit deprecates convention-based overrides and logs a WARNING whey the are encountered. For example, the following message is logged for a test that still uses convention-based overrides. A log message is generated for each such attribute in a given annotation but only once per application run. WARN Support for convention-based annotation attribute overrides is deprecated and will be removed in Spring Framework 6.1. Please annotate the 'basePackages' attribute in @org.springframework.context.annotation.ConfigurationClassPostProcessorTests$ComposedConfigurationWithAttributeOverrides with an appropriate @AliasFor declaration. Closes gh-28760 --- .../annotation/AnnotationTypeMapping.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java index 35970f018e..9fb874dc1b 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java @@ -28,6 +28,10 @@ import java.util.LinkedHashSet; import java.util.List; 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.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet; import org.springframework.lang.Nullable; @@ -46,6 +50,17 @@ import org.springframework.util.StringUtils; */ final class AnnotationTypeMapping { + private static final Log logger = LogFactory.getLog(AnnotationTypeMapping.class); + + /** + * Set of fully qualified class names concatenated with attribute names for + * annotations which we have already checked for use of convention-based + * annotation attribute overrides. + * @since 6.0 + * @see #addConventionMappings() + */ + private static final Set conventionBasedOverrideCheckCache = ConcurrentHashMap.newKeySet(); + private static final MirrorSet[] EMPTY_MIRROR_SETS = new MirrorSet[0]; @@ -273,6 +288,21 @@ final class AnnotationTypeMapping { String name = this.attributes.get(i).getName(); int mapped = rootAttributes.indexOf(name); if (!MergedAnnotation.VALUE.equals(name) && mapped != -1 && !isExplicitAttributeOverride(name)) { + String rootAnnotationTypeName = this.root.annotationType.getName(); + // We want to avoid duplicate log warnings as much as possible, without + // fully synchronizing on the cache. + String cacheKey = rootAnnotationTypeName + "." + name; + if (!conventionBasedOverrideCheckCache.contains(cacheKey)) { + conventionBasedOverrideCheckCache.add(cacheKey); + if (logger.isWarnEnabled()) { + logger.warn(""" + Support for convention-based annotation attribute overrides is \ + deprecated and will be removed in Spring Framework 6.1. Please \ + annotate the '%s' attribute in @%s with an appropriate @AliasFor \ + declaration.""" + .formatted(name, rootAnnotationTypeName)); + } + } mappings[i] = mapped; MirrorSet mirrors = getMirrorSets().getAssigned(i); if (mirrors != null) {