diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java index fce5383e7c..a0133f88fa 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java @@ -174,7 +174,9 @@ class BeanDefinitionPropertiesCodeGenerator { Map indexedValues = constructorValues.getIndexedArgumentValues(); if (!indexedValues.isEmpty()) { indexedValues.forEach((index, valueHolder) -> { - CodeBlock valueCode = generateValue(valueHolder.getName(), valueHolder.getValue()); + Object value = valueHolder.getValue(); + CodeBlock valueCode = castIfNecessary(value == null, Object.class, + generateValue(valueHolder.getName(), value)); code.addStatement( "$L.getConstructorArgumentValues().addIndexedArgumentValue($L, $L)", BEAN_DEFINITION_VARIABLE, index, valueCode); @@ -346,6 +348,20 @@ class BeanDefinitionPropertiesCodeGenerator { } } + /** + * Cast the specified {@code valueCode} to the specified {@code castType} if + * the {@code castNecessary} is {@code true}. Otherwise return the valueCode + * as is. + * @param castNecessary whether a cast is necessary + * @param castType the type to cast to + * @param valueCode the code for the value + * @return the existing value or a form of {@code (CastType) valueCode} if a + * cast is necessary + */ + private CodeBlock castIfNecessary(boolean castNecessary, Class castType, CodeBlock valueCode) { + return (castNecessary ? CodeBlock.of("($T) $L", castType, valueCode) : valueCode); + } + static class PropertyNamesStack { private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(ArrayDeque::new); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java index 8c736e25ed..bd58567226 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java @@ -235,6 +235,18 @@ class BeanDefinitionPropertiesCodeGeneratorTests { }); } + @Test + void constructorArgumentValuesWhenIndexedNullValue() { + this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null); + compile((actual, compiled) -> { + ConstructorArgumentValues argumentValues = actual.getConstructorArgumentValues(); + Map values = argumentValues.getIndexedArgumentValues(); + assertThat(values.get(0)).satisfies(assertValueHolder(null, null, null)); + assertThat(values).hasSize(1); + assertThat(argumentValues.getGenericArgumentValues()).isEmpty(); + }); + } + @Test void constructorArgumentValuesWhenGenericValuesWithName() { this.beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(String.class); @@ -255,7 +267,9 @@ class BeanDefinitionPropertiesCodeGeneratorTests { }); } - private Consumer assertValueHolder(Object value, @Nullable Class type, @Nullable String name) { + private Consumer assertValueHolder( + @Nullable Object value, @Nullable Class type, @Nullable String name) { + return valueHolder -> { assertThat(valueHolder.getValue()).isEqualTo(value); assertThat(valueHolder.getType()).isEqualTo((type != null ? type.getName() : null));