Merge branch '7.0.x'

This commit is contained in:
Sam Brannen
2026-09-30 13:43:28 +02:00
3 changed files with 96 additions and 1 deletions
@@ -493,7 +493,9 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
private Object adaptForAttribute(Method attribute, Object value) {
Class<?> attributeType = ClassUtils.resolvePrimitiveIfNecessary(attribute.getReturnType());
if (attributeType.isArray() && !value.getClass().isArray()) {
Object array = Array.newInstance(value.getClass(), 1);
Object array = ClassUtils.isAssignable(attributeType.getComponentType(), value.getClass()) ?
Array.newInstance(attributeType.getComponentType(), 1) :
Array.newInstance(value.getClass(), 1);
Array.set(array, 0, value);
return adaptForAttribute(attribute, array);
}
@@ -596,6 +596,19 @@ class MergedAnnotationsTests {
assertThat(annotation.getClassArray("classes")).isEmpty();
}
@Test
void getWithSingleValuePrimitiveAttributesOverridingPrimitiveArraysViaAliasFor() {
MergedAnnotation<PrimitiveArrays> annotation = MergedAnnotations.from(ComposedPrimitiveSingleValuesClass.class)
.get(PrimitiveArrays.class);
assertThat(annotation.isPresent()).isTrue();
assertThat(annotation.getIntArray("ints")).containsExactly(42);
assertThat(annotation.getLongArray("longs")).containsExactly(42L);
assertThat(annotation.getBooleanArray("booleans")).containsExactly(true);
assertThat(annotation.getCharArray("chars")).containsExactly('c');
assertThat(annotation.getDoubleArray("doubles")).containsExactly(4.2d);
assertThat(annotation.synthesize().ints()).containsExactly(42);
}
@Test
void getWithInheritedAnnotationsFromShadowedAliasComposedAnnotation() {
MergedAnnotation<?> annotation = MergedAnnotations.from(
@@ -2301,6 +2314,44 @@ class MergedAnnotationsTests {
@interface ComposedTransactionalComponent {
}
@Retention(RetentionPolicy.RUNTIME)
@interface PrimitiveArrays {
int[] ints() default {};
long[] longs() default {};
boolean[] booleans() default {};
char[] chars() default {};
double[] doubles() default {};
}
@PrimitiveArrays
@Retention(RetentionPolicy.RUNTIME)
@interface ComposedPrimitiveSingleValues {
@AliasFor(annotation = PrimitiveArrays.class, attribute = "ints")
int anInt() default 0;
@AliasFor(annotation = PrimitiveArrays.class, attribute = "longs")
long aLong() default 0L;
@AliasFor(annotation = PrimitiveArrays.class, attribute = "booleans")
boolean aBoolean() default false;
@AliasFor(annotation = PrimitiveArrays.class, attribute = "chars")
char aChar() default 'x';
@AliasFor(annotation = PrimitiveArrays.class, attribute = "doubles")
double aDouble() default 0.0d;
}
@ComposedPrimitiveSingleValues(anInt = 42, aLong = 42L, aBoolean = true, aChar = 'c', aDouble = 4.2d)
static class ComposedPrimitiveSingleValuesClass {
}
static class NonAnnotatedClass {
}
@@ -20,6 +20,7 @@ import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
@@ -43,6 +44,7 @@ import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
*
* @author Phillip Webb
* @author Sam Brannen
* @author Chengang Guan
*/
class TypeMappedAnnotationTests {
@@ -110,6 +112,46 @@ class TypeMappedAnnotationTests {
assertThat(annotation.getValue("enumValue")).contains(new ExampleEnum[] {});
}
@Test
@SuppressWarnings("unchecked")
void adaptFromSingleValueToArray() {
Map<String, Object> attributes = new HashMap<>();
attributes.put("stringValue", "s");
attributes.put("byteValue", (byte) 1);
attributes.put("shortValue", (short) 1);
attributes.put("intValue", 1);
attributes.put("longValue", 1L);
attributes.put("booleanValue", true);
attributes.put("charValue", 'c');
attributes.put("doubleValue", 1.0d);
attributes.put("floatValue", 1.0f);
attributes.put("classValue", String.class);
attributes.put("enumValue", ExampleEnum.ONE);
attributes.put("annotationValue", TypeMappedAnnotation.of(null, null,
ExplicitMirror.class, Collections.singletonMap("a", "singleValue")));
MergedAnnotation<ArrayTypes> annotation = TypeMappedAnnotation.of(null, null, ArrayTypes.class, attributes);
assertThat(annotation.getValue("stringValue")).contains(new String[] {"s"});
assertThat(annotation.getValue("byteValue")).contains(new byte[] {1});
assertThat(annotation.getValue("shortValue")).contains(new short[] {1});
assertThat(annotation.getValue("intValue")).contains(new int[] {1});
assertThat(annotation.getValue("longValue")).contains(new long[] {1L});
assertThat(annotation.getValue("booleanValue")).contains(new boolean[] {true});
assertThat(annotation.getValue("charValue")).contains(new char[] {'c'});
assertThat(annotation.getValue("doubleValue")).contains(new double[] {1.0d});
assertThat(annotation.getValue("floatValue")).contains(new float[] {1.0f});
assertThat(annotation.getValue("classValue")).contains(new Class<?>[] {String.class});
assertThat(annotation.getValue("enumValue")).contains(new ExampleEnum[] {ExampleEnum.ONE});
assertThat(annotation.getValue("annotationValue")).hasValueSatisfying(value -> {
assertThat(value.getClass().isArray()).isTrue();
assertThat(Array.getLength(value)).isEqualTo(1);
Object element = Array.get(value, 0);
MergedAnnotation<ExplicitMirror> mirror = (MergedAnnotation<ExplicitMirror>) element;
assertThat(mirror.getString("a")).isEqualTo("singleValue");
assertThat(mirror.getString("b")).isEqualTo("singleValue");
});
}
@Test
void adaptFromNestedMergedAnnotation() {
MergedAnnotation<NestedAnnotation> nested = MergedAnnotation.of(NestedAnnotation.class);