From baf970d2b2222560b02ac69f86d9fec7bfc50842 Mon Sep 17 00:00:00 2001 From: seonwoo_jung <79202163+seonwooj0810@users.noreply.github.com> Date: Sat, 11 Jul 2026 09:19:55 +0900 Subject: [PATCH] Do not render meta-annotations for AOT repository method parameters. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `MethodMetadata.buildParameter(…)` iterates `MergedAnnotations.from(parameterAnnotations)`, which also exposes meta-annotations. As a result a parameter annotated with, e.g., `jakarta.validation.constraints.@NotNull` caused its meta-annotation `@jakarta.validation.Constraint` to be rendered standalone into the generated repository source, producing uncompilable code (`annotation @jakarta.validation.Constraint is missing a default value for the element 'validatedBy'`) and failing AOT/native processing. Only render directly-present annotations; meta-annotations remain discoverable through the concrete annotation itself. Closes #3499 Signed-off-by: seonwoo_jung <79202163+seonwooj0810@users.noreply.github.com> --- .../aot/generate/MethodMetadata.java | 4 ++- .../aot/generate/MethodMetadataUnitTests.java | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/repository/aot/generate/MethodMetadata.java b/src/main/java/org/springframework/data/repository/aot/generate/MethodMetadata.java index f9b657c31e..fdb343335c 100644 --- a/src/main/java/org/springframework/data/repository/aot/generate/MethodMetadata.java +++ b/src/main/java/org/springframework/data/repository/aot/generate/MethodMetadata.java @@ -108,7 +108,9 @@ private static ParameterSpec buildParameter(TypeName parameterType, MethodParame MergedAnnotations annotations = MergedAnnotations.from(methodParameter.getParameterAnnotations()); for (MergedAnnotation annotation : annotations) { - builder.addAnnotation(AnnotationSpec.get(annotation.synthesize())); + if (annotation.isDirectlyPresent()) { + builder.addAnnotation(AnnotationSpec.get(annotation.synthesize())); + } } return builder.build(); diff --git a/src/test/java/org/springframework/data/repository/aot/generate/MethodMetadataUnitTests.java b/src/test/java/org/springframework/data/repository/aot/generate/MethodMetadataUnitTests.java index 98dd813839..dbccca4c8d 100644 --- a/src/test/java/org/springframework/data/repository/aot/generate/MethodMetadataUnitTests.java +++ b/src/test/java/org/springframework/data/repository/aot/generate/MethodMetadataUnitTests.java @@ -18,6 +18,10 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Method; import org.junit.jupiter.api.Test; @@ -61,6 +65,16 @@ void addsAnnotations() throws NoSuchMethodException { .containsExactly(SortDefault.class.getTypeName(), Param.class.getTypeName()); } + @Test // GH-3499 + void doesNotRenderMetaAnnotations() throws NoSuchMethodException { + + MethodMetadata metadata = methodMetadataFor("metaAnnotatedArgMethod"); + + ParameterSpec arg0 = metadata.getMethodArguments().get("arg0"); + assertThat(arg0.annotations()).extracting(annotationSpec -> annotationSpec.type().toString()) + .containsExactly(MetaAnnotated.class.getCanonicalName()); + } + @Test // GH-3270 void getParameterNameByNonExistingIndex() throws NoSuchMethodException { @@ -100,5 +114,16 @@ private interface DummyRepo { String noArgsMethod(); String threeArgsMethod(Object arg0, @SortDefault Pageable arg1, @SortDefault @Param("foo") Object arg2); + + String metaAnnotatedArgMethod(@MetaAnnotated Object arg0); } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.ANNOTATION_TYPE) + private @interface Meta {} + + @Meta + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + private @interface MetaAnnotated {} }