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
This commit is contained in:
Sam Brannen 2022-07-07 17:36:16 +02:00
parent 81acbe7e2f
commit a68f5b1674
1 changed files with 30 additions and 0 deletions

View File

@ -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<String> 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) {