Browse Source

Render method parameter annotations in AOT repositories.

We now include annotations of repository query method parameters to allow introspection through Parameters for reflective checks.

See spring-projects/spring-data-relational#2245
issue/param-annotations
Mark Paluch 1 month ago
parent
commit
6e5a18609f
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 20
      src/main/java/org/springframework/data/repository/aot/generate/MethodMetadata.java
  2. 3
      src/test/java/example/UserRepository.java
  3. 6
      src/test/java/org/springframework/data/repository/aot/generate/AotRepositoryMethodBuilderUnitTests.java
  4. 13
      src/test/java/org/springframework/data/repository/aot/generate/MethodMetadataUnitTests.java

20
src/main/java/org/springframework/data/repository/aot/generate/MethodMetadata.java

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package org.springframework.data.repository.aot.generate;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
@ -29,9 +30,12 @@ import org.jspecify.annotations.Nullable; @@ -29,9 +30,12 @@ import org.jspecify.annotations.Nullable;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.data.core.TypeInformation;
import org.springframework.data.javapoet.TypeNames;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.javapoet.AnnotationSpec;
import org.springframework.javapoet.ParameterSpec;
import org.springframework.javapoet.TypeName;
import org.springframework.util.Assert;
@ -87,7 +91,7 @@ class MethodMetadata { @@ -87,7 +91,7 @@ class MethodMetadata {
TypeName parameterType = parameterTypeName(methodParameter, repositoryInterfaceType);
Assert.notNull(methodParameter.getParameterName(), "MethodParameter.getParameterName() must not be null");
ParameterSpec parameterSpec = ParameterSpec.builder(parameterType, methodParameter.getParameterName()).build();
ParameterSpec parameterSpec = buildParameter(parameterType, methodParameter);
if (methodArguments.containsKey(parameterSpec.name())) {
throw new IllegalStateException("Parameter with name '" + parameterSpec.name() + "' already exists.");
@ -98,7 +102,19 @@ class MethodMetadata { @@ -98,7 +102,19 @@ class MethodMetadata {
}
}
@SuppressWarnings("NullAway")
private static ParameterSpec buildParameter(TypeName parameterType, MethodParameter methodParameter) {
ParameterSpec.Builder builder = ParameterSpec.builder(parameterType, methodParameter.getParameterName());
MergedAnnotations annotations = MergedAnnotations.from(methodParameter.getParameterAnnotations());
for (MergedAnnotation<Annotation> annotation : annotations) {
builder.addAnnotation(AnnotationSpec.get(annotation.synthesize()));
}
return builder.build();
}
@SuppressWarnings("NullAway")
static TypeName parameterTypeName(MethodParameter methodParameter, Class<?> repositoryInterface) {
ResolvableType resolvableParameterType = ResolvableType.forMethodParameter(methodParameter);

3
src/test/java/example/UserRepository.java

@ -20,13 +20,14 @@ import example.UserRepository.User; @@ -20,13 +20,14 @@ import example.UserRepository.User;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
/**
* @author Christoph Strobl
*/
public interface UserRepository extends CrudRepository<User, Long>, UserRepositoryExtension {
User findByFirstname(String firstname);
User findByFirstname(@Param("hello") String firstname);
List<User> findByFirstnameIn(List<String> firstnames);

6
src/test/java/org/springframework/data/repository/aot/generate/AotRepositoryMethodBuilderUnitTests.java

@ -55,7 +55,7 @@ class AotRepositoryMethodBuilderUnitTests { @@ -55,7 +55,7 @@ class AotRepositoryMethodBuilderUnitTests {
when(methodGenerationContext.getExpressionMarker()).thenReturn(new ExpressionMarker());
}
@Test // GH-3279
@Test // GH-3279, GH-3458
void generatesMethodSkeletonBasedOnGenerationMetadata() throws NoSuchMethodException {
Method method = UserRepository.class.getMethod("findByFirstname", String.class);
@ -66,8 +66,8 @@ class AotRepositoryMethodBuilderUnitTests { @@ -66,8 +66,8 @@ class AotRepositoryMethodBuilderUnitTests {
when(methodGenerationContext.getTargetMethodMetadata()).thenReturn(methodMetadata);
AotRepositoryMethodBuilder builder = new AotRepositoryMethodBuilder(methodGenerationContext);
assertThat(builder.buildMethod().toString()) //
.containsPattern("public .*User findByFirstname\\(.*String firstname\\)");
assertThat(builder.buildMethod().toString().replaceAll(System.lineSeparator(), " ")) //
.containsPattern("findByFirstname\\(.*@.*Param\\(\"hello\"\\).*String firstname\\)");
}
@Test // GH-3279

13
src/test/java/org/springframework/data/repository/aot/generate/MethodMetadataUnitTests.java

@ -26,6 +26,8 @@ import org.mockito.Mockito; @@ -26,6 +26,8 @@ import org.mockito.Mockito;
import org.springframework.data.core.TypeInformation;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.query.Param;
import org.springframework.javapoet.ParameterSpec;
/**
* Unit tests for {@link MethodMetadata}.
@ -44,6 +46,15 @@ class MethodMetadataUnitTests { @@ -44,6 +46,15 @@ class MethodMetadataUnitTests {
assertThat(metadata.getParameterName(2)).isEqualTo("arg2");
}
@Test // GH-3458
void addsAnnotations() throws NoSuchMethodException {
MethodMetadata metadata = methodMetadataFor("threeArgsMethod");
ParameterSpec spec = metadata.getMethodArguments().get("arg2");
assertThat(spec.annotations()).hasSize(1);
}
@Test // GH-3270
void getParameterNameByNonExistingIndex() throws NoSuchMethodException {
@ -82,6 +93,6 @@ class MethodMetadataUnitTests { @@ -82,6 +93,6 @@ class MethodMetadataUnitTests {
String noArgsMethod();
String threeArgsMethod(Object arg0, Pageable arg1, Object arg2);
String threeArgsMethod(Object arg0, Pageable arg1, @Param("foo") Object arg2);
}
}

Loading…
Cancel
Save