mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-10-01 03:39:05 +00:00
Fix single-value adaptation for primitive array types in TypeMappedAnnotation
Prior to this commit, `adaptForAttribute(Method, Object)` created the wrapping array from `value.getClass()` when a single non-array value was provided for an array attribute. This worked for object array types but failed for primitive array types: wrapping a boxed value produced a boxed array, which then failed the compatibility check and threw an `IllegalStateException`. This commit derives the component type from the declared attribute type when it is assignable from the value type, falling back to `value.getClass()` otherwise. The existing adaptation path for object array types is therefore preserved, and all primitive array types now accept a single value. The accompanying test covers single-value wrapping for every array type declared by ArrayTypes. Closes gh-37349 Signed-off-by: Chengang Guan <guanchengang@qq.com>
This commit is contained in:
+3
-1
@@ -488,7 +488,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);
|
||||
}
|
||||
|
||||
+42
@@ -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;
|
||||
@@ -42,6 +43,7 @@ import static org.assertj.core.api.InstanceOfAssertFactories.throwable;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @author Chengang Guan
|
||||
*/
|
||||
class TypeMappedAnnotationTests {
|
||||
|
||||
@@ -109,6 +111,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);
|
||||
|
||||
Reference in New Issue
Block a user