Merge branch '5.3.x'

This commit is contained in:
Sam Brannen 2022-07-06 13:20:16 +02:00
commit c54b1519e4
2 changed files with 87 additions and 80 deletions

View File

@ -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.
@ -36,6 +36,7 @@ import javax.annotation.meta.When;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.AnnotationUtilsTests.ExtendsBaseClassWithGenericAnnotatedMethod;
@ -82,6 +83,90 @@ class AnnotatedElementUtilsTests {
private static final String TX_NAME = Transactional.class.getName();
@Nested
class ConventionBasedAnnotationAttributeOverrideTests {
@Test
void getMergedAnnotationAttributesWithConventionBasedComposedAnnotation() {
Class<?> element = ConventionBasedComposedContextConfigClass.class;
String name = ContextConfig.class.getName();
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name);
assertThat(attributes).as("Should find @ContextConfig on " + element.getSimpleName()).isNotNull();
assertThat(attributes.getStringArray("locations")).as("locations").containsExactly("explicitDeclaration");
assertThat(attributes.getStringArray("value")).as("value").containsExactly("explicitDeclaration");
// Verify contracts between utility methods:
assertThat(isAnnotated(element, name)).isTrue();
}
/**
* This test should never pass, simply because Spring does not support a hybrid
* approach for annotation attribute overrides with transitive implicit aliases.
* See SPR-13554 for details.
* <p>Furthermore, if you choose to execute this test, it can fail for either
* the first test class or the second one (with different exceptions), depending
* on the order in which the JVM returns the attribute methods via reflection.
*/
@Disabled("Permanently disabled but left in place for illustrative purposes")
@Test
void getMergedAnnotationAttributesWithHalfConventionBasedAndHalfAliasedComposedAnnotation() {
for (Class<?> clazz : asList(HalfConventionBasedAndHalfAliasedComposedContextConfigClassV1.class,
HalfConventionBasedAndHalfAliasedComposedContextConfigClassV2.class)) {
getMergedAnnotationAttributesWithHalfConventionBasedAndHalfAliasedComposedAnnotation(clazz);
}
}
private void getMergedAnnotationAttributesWithHalfConventionBasedAndHalfAliasedComposedAnnotation(Class<?> clazz) {
String name = ContextConfig.class.getName();
String simpleName = clazz.getSimpleName();
AnnotationAttributes attributes = getMergedAnnotationAttributes(clazz, name);
assertThat(attributes).as("Should find @ContextConfig on " + simpleName).isNotNull();
assertThat(attributes.getStringArray("locations")).as("locations for class [" + simpleName + "]")
.containsExactly("explicitDeclaration");
assertThat(attributes.getStringArray("value")).as("value for class [" + simpleName + "]")
.containsExactly("explicitDeclaration");
// Verify contracts between utility methods:
assertThat(isAnnotated(clazz, name)).isTrue();
}
@Test
void getMergedAnnotationAttributesWithInvalidConventionBasedComposedAnnotation() {
Class<?> element = InvalidConventionBasedComposedContextConfigClass.class;
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
getMergedAnnotationAttributes(element, ContextConfig.class))
.withMessageContaining("Different @AliasFor mirror values for annotation")
.withMessageContaining("attribute 'locations' and its alias 'value'")
.withMessageContaining("values of [{requiredLocationsDeclaration}] and [{duplicateDeclaration}]");
}
@Test
void findMergedAnnotationAttributesWithSingleElementOverridingAnArrayViaConvention() {
assertComponentScanAttributes(ConventionBasedSinglePackageComponentScanClass.class, "com.example.app.test");
}
@Test
void findMergedAnnotationWithLocalAliasesThatConflictWithAttributesInMetaAnnotationByConvention() {
Class<?> element = SpringAppConfigClass.class;
ContextConfig contextConfig = findMergedAnnotation(element, ContextConfig.class);
assertThat(contextConfig).as("Should find @ContextConfig on " + element).isNotNull();
assertThat(contextConfig.locations()).as("locations for " + element).isEmpty();
// 'value' in @SpringAppConfig should not override 'value' in @ContextConfig
assertThat(contextConfig.value()).as("value for " + element).isEmpty();
assertThat(contextConfig.classes()).as("classes for " + element).containsExactly(Number.class);
}
@Test
void findMergedAnnotationWithSingleElementOverridingAnArrayViaConvention() throws Exception {
assertWebMapping(WebController.class.getMethod("postMappedWithPathAttribute"));
}
}
@Test
void getMetaAnnotationTypesOnNonAnnotatedClass() {
assertThat(getMetaAnnotationTypes(NonAnnotatedClass.class, TransactionalComponent.class).isEmpty()).isTrue();
@ -363,51 +448,6 @@ class AnnotatedElementUtilsTests {
assertThat(isAnnotated(element, name)).isTrue();
}
@Test
void getMergedAnnotationAttributesWithConventionBasedComposedAnnotation() {
Class<?> element = ConventionBasedComposedContextConfigClass.class;
String name = ContextConfig.class.getName();
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name);
assertThat(attributes).as("Should find @ContextConfig on " + element.getSimpleName()).isNotNull();
assertThat(attributes.getStringArray("locations")).as("locations").isEqualTo(asArray("explicitDeclaration"));
assertThat(attributes.getStringArray("value")).as("value").isEqualTo(asArray("explicitDeclaration"));
// Verify contracts between utility methods:
assertThat(isAnnotated(element, name)).isTrue();
}
/**
* This test should never pass, simply because Spring does not support a hybrid
* approach for annotation attribute overrides with transitive implicit aliases.
* See SPR-13554 for details.
* <p>Furthermore, if you choose to execute this test, it can fail for either
* the first test class or the second one (with different exceptions), depending
* on the order in which the JVM returns the attribute methods via reflection.
*/
@Disabled("Permanently disabled but left in place for illustrative purposes")
@Test
void getMergedAnnotationAttributesWithHalfConventionBasedAndHalfAliasedComposedAnnotation() {
for (Class<?> clazz : asList(HalfConventionBasedAndHalfAliasedComposedContextConfigClassV1.class,
HalfConventionBasedAndHalfAliasedComposedContextConfigClassV2.class)) {
getMergedAnnotationAttributesWithHalfConventionBasedAndHalfAliasedComposedAnnotation(clazz);
}
}
private void getMergedAnnotationAttributesWithHalfConventionBasedAndHalfAliasedComposedAnnotation(Class<?> clazz) {
String[] expected = asArray("explicitDeclaration");
String name = ContextConfig.class.getName();
String simpleName = clazz.getSimpleName();
AnnotationAttributes attributes = getMergedAnnotationAttributes(clazz, name);
assertThat(attributes).as("Should find @ContextConfig on " + simpleName).isNotNull();
assertThat(attributes.getStringArray("locations")).as("locations for class [" + clazz.getSimpleName() + "]").isEqualTo(expected);
assertThat(attributes.getStringArray("value")).as("value for class [" + clazz.getSimpleName() + "]").isEqualTo(expected);
// Verify contracts between utility methods:
assertThat(isAnnotated(clazz, name)).isTrue();
}
@Test
void getMergedAnnotationAttributesWithAliasedComposedAnnotation() {
Class<?> element = AliasedComposedContextConfigClass.class;
@ -530,16 +570,6 @@ class AnnotatedElementUtilsTests {
assertThat(isAnnotated(element, name)).isTrue();
}
@Test
void getMergedAnnotationAttributesWithInvalidConventionBasedComposedAnnotation() {
Class<?> element = InvalidConventionBasedComposedContextConfigClass.class;
assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy(() ->
getMergedAnnotationAttributes(element, ContextConfig.class))
.withMessageContaining("Different @AliasFor mirror values for annotation")
.withMessageContaining("attribute 'locations' and its alias 'value'")
.withMessageContaining("values of [{requiredLocationsDeclaration}] and [{duplicateDeclaration}]");
}
@Test
void getMergedAnnotationAttributesWithShadowedAliasComposedAnnotation() {
Class<?> element = ShadowedAliasComposedContextConfigClass.class;
@ -743,11 +773,6 @@ class AnnotatedElementUtilsTests {
assertThat(annotation.qualifier()).as("TX qualifier for " + clazz).isEqualTo("anotherTransactionManager");
}
@Test
void findMergedAnnotationAttributesWithSingleElementOverridingAnArrayViaConvention() {
assertComponentScanAttributes(ConventionBasedSinglePackageComponentScanClass.class, "com.example.app.test");
}
@Test
void findMergedAnnotationAttributesWithSingleElementOverridingAnArrayViaAliasFor() {
assertComponentScanAttributes(AliasForBasedSinglePackageComponentScanClass.class, "com.example.app.test");
@ -800,24 +825,6 @@ class AnnotatedElementUtilsTests {
assertThat(testPropSource.value()).as("value").isEqualTo(propFiles);
}
@Test
void findMergedAnnotationWithLocalAliasesThatConflictWithAttributesInMetaAnnotationByConvention() {
final String[] EMPTY = new String[0];
Class<?> element = SpringAppConfigClass.class;
ContextConfig contextConfig = findMergedAnnotation(element, ContextConfig.class);
assertThat(contextConfig).as("Should find @ContextConfig on " + element).isNotNull();
assertThat(contextConfig.locations()).as("locations for " + element).isEqualTo(EMPTY);
// 'value' in @SpringAppConfig should not override 'value' in @ContextConfig
assertThat(contextConfig.value()).as("value for " + element).isEqualTo(EMPTY);
assertThat(contextConfig.classes()).as("classes for " + element).isEqualTo(new Class<?>[] {Number.class});
}
@Test
void findMergedAnnotationWithSingleElementOverridingAnArrayViaConvention() throws Exception {
assertWebMapping(WebController.class.getMethod("postMappedWithPathAttribute"));
}
@Test
void findMergedAnnotationWithSingleElementOverridingAnArrayViaAliasFor() throws Exception {
assertWebMapping(WebController.class.getMethod("getMappedWithValueAttribute"));

View File

@ -232,7 +232,7 @@ class MergedAnnotationsTests {
}
@Test
void withInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnnotation2() {
void getWithInheritedAnnotationsFromHalfConventionBasedAndHalfAliasedComposedAnnotation2() {
// SPR-13554: convention mapping mixed with AliasFor annotations
// locations doesn't apply because it has no AliasFor annotation
MergedAnnotation<?> annotation =