diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java index 6d9c1bcf5d..8c909a8a5b 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java @@ -477,6 +477,15 @@ public interface MergedAnnotation { */ > T asMap(Function, T> factory, Adapt... adaptations); + /** + * Determine if this merged annotation is synthesizable. + *

Consult the documentation for {@link #synthesize()} for an explanation + * of what is considered synthesizable. + * @return {@code true} if the mapped annotation is synthesizable + * @since 6.0 + */ + boolean isSynthesizable(); + /** * Create a type-safe synthesized version of this merged annotation that can * be used directly in code. diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java index 0c7c9abad6..75966992a4 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -134,6 +134,11 @@ final class MissingMergedAnnotation extends AbstractMerged return factory.apply(this); } + @Override + public boolean isSynthesizable() { + return false; + } + @Override public String toString() { return "(missing)"; diff --git a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java index 7457ddd6de..1fcbb18883 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java @@ -319,6 +319,17 @@ final class TypeMappedAnnotation extends AbstractMergedAnn return value; } + @Override + public boolean isSynthesizable() { + // Is this a mapped annotation for a composed annotation, and are there + // annotation attributes (mirrors) that need to be merged? + if (getDistance() > 0 && this.resolvedMirrors.length > 0) { + return true; + } + // Is the mapped annotation itself synthesizable? + return this.mapping.isSynthesizable(); + } + @Override @SuppressWarnings("unchecked") protected A createSynthesizedAnnotation() { @@ -347,22 +358,15 @@ final class TypeMappedAnnotation extends AbstractMergedAnn * Determine if the supplied annotation has not already been synthesized * and whether the mapped annotation is a composed annotation * that needs to have its attributes merged or the mapped annotation is - * {@linkplain AnnotationTypeMapping#isSynthesizable() synthesizable} in general. + * {@linkplain #isSynthesizable() synthesizable} in general. * @param annotation the annotation to check * @since 5.3.22 */ private boolean isSynthesizable(Annotation annotation) { - // Already synthesized? if (annotation instanceof SynthesizedAnnotation) { return false; } - // Is this a mapped annotation for a composed annotation, and are there - // annotation attributes (mirrors) that need to be merged? - if (getDistance() > 0 && this.resolvedMirrors.length > 0) { - return true; - } - // Is the mapped annotation itself synthesizable? - return this.mapping.isSynthesizable(); + return isSynthesizable(); } @Override diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index 0b78f39e37..94efc1e279 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -1504,6 +1504,13 @@ class MergedAnnotationsTests { assertThat(synthesizedComponent.value()).isEqualTo("webController"); } + @Test + void isSynthesizableWithoutAttributeAliases() throws Exception { + Component component = WebController.class.getAnnotation(Component.class); + assertThat(component).isNotNull(); + assertThat(MergedAnnotation.from(component).isSynthesizable()).isFalse(); + } + @Test void synthesizeAlreadySynthesized() throws Exception { Method method = WebController.class.getMethod("handleMappedWithValueAttribute"); @@ -1567,10 +1574,16 @@ class MergedAnnotationsTests { void synthesizeShouldNotSynthesizeNonsynthesizableAnnotationsWhenUsingMergedAnnotationsFromApi() { MergedAnnotations mergedAnnotations = MergedAnnotations.from(SecurityConfig.class); - EnableWebSecurity enableWebSecurity = mergedAnnotations.get(EnableWebSecurity.class).synthesize(); + MergedAnnotation enableWebSecurityAnnotation = + mergedAnnotations.get(EnableWebSecurity.class); + assertThat(enableWebSecurityAnnotation.isSynthesizable()).isFalse(); + EnableWebSecurity enableWebSecurity = enableWebSecurityAnnotation.synthesize(); assertThat(enableWebSecurity).isNotInstanceOf(SynthesizedAnnotation.class); - EnableGlobalAuthentication enableGlobalAuthentication = mergedAnnotations.get(EnableGlobalAuthentication.class).synthesize(); + MergedAnnotation enableGlobalAuthenticationMergedAnnotation = + mergedAnnotations.get(EnableGlobalAuthentication.class); + assertThat(enableGlobalAuthenticationMergedAnnotation.isSynthesizable()).isFalse(); + EnableGlobalAuthentication enableGlobalAuthentication = enableGlobalAuthenticationMergedAnnotation.synthesize(); assertThat(enableGlobalAuthentication).isNotInstanceOf(SynthesizedAnnotation.class); } @@ -1718,8 +1731,9 @@ class MergedAnnotationsTests { ImplicitAliasesTestConfiguration config = clazz.getAnnotation( ImplicitAliasesTestConfiguration.class); assertThat(config).isNotNull(); - ImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from( - config).synthesize(); + MergedAnnotation mergedAnnotation = MergedAnnotation.from(config); + assertThat(mergedAnnotation.isSynthesizable()).isTrue(); + ImplicitAliasesTestConfiguration synthesized = mergedAnnotation.synthesize(); assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class); assertThat(synthesized.value()).isEqualTo(expected); assertThat(synthesized.location1()).isEqualTo(expected); @@ -1746,8 +1760,11 @@ class MergedAnnotationsTests { ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration config = clazz.getAnnotation( ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration.class); assertThat(config).isNotNull(); + MergedAnnotation mergedAnnotation = + MergedAnnotation.from(config); + assertThat(mergedAnnotation.isSynthesizable()).isTrue(); ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration synthesized = - MergedAnnotation.from(config).synthesize(); + mergedAnnotation.synthesize(); assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class); assertThat(synthesized.value()).isEqualTo(expected); assertThat(synthesized.location()).isEqualTo(expected); diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java index 151b31f3ae..eb391f4c1c 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java @@ -255,6 +255,11 @@ class MissingMergedAnnotationTests { assertThat(this.missing.getDefaultValue("value", Integer.class)).isEmpty(); } + @Test + void isSynthesizableReturnsFalse() { + assertThat(this.missing.isSynthesizable()).isFalse(); + } + @Test void synthesizeThrowsNoSuchElementException() { assertThatNoSuchElementException().isThrownBy(this.missing::synthesize);