Browse Source

Register native hints for (at)Reflective annotated methods of domain types.

Closes: #3387
Original Pull Request: #3391
pull/3394/head
Christoph Strobl 2 months ago
parent
commit
f841cac51c
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 6
      src/main/java/org/springframework/data/aot/DefaultAotContext.java
  2. 8
      src/main/java/org/springframework/data/util/TypeContributor.java
  3. 148
      src/test/java/org/springframework/data/aot/DefaultAotContextUnitTests.java

6
src/main/java/org/springframework/data/aot/DefaultAotContext.java

@ -64,7 +64,7 @@ class DefaultAotContext implements AotContext { @@ -64,7 +64,7 @@ class DefaultAotContext implements AotContext {
private final Map<Class<?>, ContextualTypeConfiguration> typeConfigurations = new HashMap<>();
private final Environment environment;
private final ReflectiveRuntimeHintsRegistrar runtimeHintsRegistrar = new ReflectiveRuntimeHintsRegistrar();
private final ReflectiveRuntimeHintsRegistrar reflectiveRuntimeHintsRegistrar = new ReflectiveRuntimeHintsRegistrar();
public DefaultAotContext(BeanFactory beanFactory, Environment environment) {
this(beanFactory, environment, new AotMappingContext());
@ -250,6 +250,9 @@ class DefaultAotContext implements AotContext { @@ -250,6 +250,9 @@ class DefaultAotContext implements AotContext {
categories.toArray(MemberCategory[]::new));
}
// check types for presence of @Reflective annotation
reflectiveRuntimeHintsRegistrar.registerRuntimeHints(generationContext.getRuntimeHints(), type);
if (contributeAccessors) {
AccessorContributionConfiguration configuration = AccessorContributionConfiguration.of(environment);
@ -259,7 +262,6 @@ class DefaultAotContext implements AotContext { @@ -259,7 +262,6 @@ class DefaultAotContext implements AotContext {
}
if (forDataBinding) {
runtimeHintsRegistrar.registerRuntimeHints(generationContext.getRuntimeHints(), type);
TypeContributor.contribute(type, Set.of(TypeContributor.DATA_NAMESPACE), generationContext);
}

8
src/main/java/org/springframework/data/util/TypeContributor.java

@ -22,7 +22,7 @@ import java.util.function.Predicate; @@ -22,7 +22,7 @@ import java.util.function.Predicate;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
import org.springframework.core.annotation.MergedAnnotation;
/**
@ -32,7 +32,8 @@ import org.springframework.core.annotation.MergedAnnotation; @@ -32,7 +32,8 @@ import org.springframework.core.annotation.MergedAnnotation;
public class TypeContributor {
public static final String DATA_NAMESPACE = "org.springframework.data";
public static final BindingReflectionHintsRegistrar REGISTRAR = new BindingReflectionHintsRegistrar();
public static final BindingReflectionHintsRegistrar DATA_BINDING_REGISTRAR = new BindingReflectionHintsRegistrar();
public static final ReflectiveRuntimeHintsRegistrar REFLECTIVE_REGISTRAR = new ReflectiveRuntimeHintsRegistrar();
/**
* Contribute the type with default reflection configuration, skip annotations.
@ -67,7 +68,8 @@ public class TypeContributor { @@ -67,7 +68,8 @@ public class TypeContributor {
return;
}
REGISTRAR.registerReflectionHints(contribution.getRuntimeHints().reflection(), type);
DATA_BINDING_REGISTRAR.registerReflectionHints(contribution.getRuntimeHints().reflection(), type);
REFLECTIVE_REGISTRAR.registerRuntimeHints(contribution.getRuntimeHints(), type);
}
/**

148
src/test/java/org/springframework/data/aot/DefaultAotContextUnitTests.java

@ -0,0 +1,148 @@ @@ -0,0 +1,148 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.aot;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.annotation.Reflective;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.mock.env.MockEnvironment;
/**
* Unit tests targeting {@link DefaultAotContext};
*
* @author Christoph Strobl
*/
@MockitoSettings(strictness = org.mockito.quality.Strictness.LENIENT)
public class DefaultAotContextUnitTests {
@Mock BeanFactory beanFactory;
@Mock AotMappingContext mappingContext;
MockEnvironment mockEnvironment = new MockEnvironment();
@Test // GH-3387
void doesNotRegisterReflectionWhenThereIsNothingToRegister() {
DefaultAotContext context = new DefaultAotContext(beanFactory, mockEnvironment, mappingContext);
context.typeConfiguration(Dummy.class, it -> {
// no specific action
});
TestGenerationContext generationContext = new TestGenerationContext();
context.contributeTypeConfigurations(generationContext);
assertThat(generationContext.getRuntimeHints()).matches(reflection().onType(Dummy.class).negate());
}
@Test // GH-3387
void doesNotRegisterReflectionWithCategoryAccordingly() {
DefaultAotContext context = new DefaultAotContext(beanFactory, mockEnvironment, mappingContext);
context.typeConfiguration(Dummy.class, it -> it.forReflectiveAccess(MemberCategory.ACCESS_DECLARED_FIELDS));
TestGenerationContext generationContext = new TestGenerationContext();
context.contributeTypeConfigurations(generationContext);
assertThat(generationContext.getRuntimeHints())
.matches(reflection().onType(Dummy.class).withAnyMemberCategory(MemberCategory.ACCESS_DECLARED_FIELDS));
}
@Test // GH-3387
void registerReflectionIfThereIsAnAtReflectiveAnnotation() throws NoSuchMethodException {
DefaultAotContext context = new DefaultAotContext(beanFactory, mockEnvironment, mappingContext);
context.typeConfiguration(DummyWithAtReflective.class, it -> {
});
TestGenerationContext generationContext = new TestGenerationContext();
context.contributeTypeConfigurations(generationContext);
assertThat(generationContext.getRuntimeHints())
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("reflectiveAnnotated")))
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("getValue")).negate())
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("justAMethod")).negate());
}
@Test // GH-3387
void registerReflectionForGetterSetterIfDataBindingRequested() throws NoSuchMethodException {
DefaultAotContext context = new DefaultAotContext(beanFactory, mockEnvironment, mappingContext);
context.typeConfiguration(DummyWithAtReflective.class, AotTypeConfiguration::forDataBinding);
TestGenerationContext generationContext = new TestGenerationContext();
context.contributeTypeConfigurations(generationContext);
assertThat(generationContext.getRuntimeHints())
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("reflectiveAnnotated")))
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("getValue")))
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("justAMethod")).negate());
}
@Test // GH-3387
void registerReflectionIfThereIsAnAtReflectiveAnnotationInTheSuperType() throws NoSuchMethodException {
DefaultAotContext context = new DefaultAotContext(beanFactory, mockEnvironment, mappingContext);
context.typeConfiguration(ExtendingDummyWithAtReflective.class, it -> {
});
TestGenerationContext generationContext = new TestGenerationContext();
context.contributeTypeConfigurations(generationContext);
assertThat(generationContext.getRuntimeHints())
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("reflectiveAnnotated")))
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("justAMethod")).negate())
.matches(reflection().onMethodInvocation(DummyWithAtReflective.class.getMethod("getValue")).negate());
}
static class Dummy {
String value;
public List<String> justAMethod() {
return null;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
static class DummyWithAtReflective extends Dummy {
@Reflective
public List<String> reflectiveAnnotated() {
return null;
}
}
static class ExtendingDummyWithAtReflective extends DummyWithAtReflective {}
}
Loading…
Cancel
Save