Fix detection of @DomainEvents and @AfterDomainEventPublication on native.

We now unconditionally process the aggregate root types declared on repositories for @Reflective annotations, which @DE and @ADEP got meta-annotated with.

Fixes #2939.
This commit is contained in:
Oliver Drotbohm
2023-09-21 16:12:19 +02:00
parent f9bc97f20f
commit 11f0d244a2
6 changed files with 119 additions and 22 deletions

View File

@@ -17,6 +17,7 @@ package org.springframework.data.aot;
import static org.assertj.core.api.Assertions.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Stream;
@@ -51,6 +52,16 @@ public class CodeContributionAssert extends AbstractAssert<CodeContributionAsser
return this;
}
public CodeContributionAssert contributesReflectionFor(Method... methods) {
for (Method method : methods) {
assertThat(this.actual.getRuntimeHints()).describedAs("No reflection entry found for [%s]", method)
.matches(RuntimeHintsPredicates.reflection().onMethod(method));
}
return this;
}
public CodeContributionAssert doesNotContributeReflectionFor(Class<?>... types) {
for (Class<?> type : types) {

View File

@@ -23,6 +23,7 @@ import java.util.Set;
import java.util.function.Consumer;
import org.assertj.core.api.AbstractAssert;
import org.junit.jupiter.api.function.ThrowingConsumer;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.aot.BeanRegistrationCode;
@@ -106,7 +107,7 @@ public class RepositoryRegistrationAotContributionAssert
}
public RepositoryRegistrationAotContributionAssert codeContributionSatisfies(
Consumer<CodeContributionAssert> assertWith) {
ThrowingConsumer<CodeContributionAssert> assertWith) {
BeanRegistrationCode mockBeanRegistrationCode = mock(BeanRegistrationCode.class);
@@ -114,7 +115,11 @@ public class RepositoryRegistrationAotContributionAssert
this.actual.applyTo(generationContext, mockBeanRegistrationCode);
assertWith.accept(new CodeContributionAssert(generationContext));
try {
assertWith.accept(new CodeContributionAssert(generationContext));
} catch (Throwable o_O) {
fail(o_O.getMessage(), o_O);
}
return this;
}

View File

@@ -28,22 +28,21 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.DecoratingProxy;
import org.springframework.data.aot.sample.ConfigWithCustomImplementation;
import org.springframework.data.aot.sample.ConfigWithCustomRepositoryBaseClass;
import org.springframework.data.aot.sample.ConfigWithFragments;
import org.springframework.data.aot.sample.ConfigWithQueryMethods;
import org.springframework.data.aot.sample.*;
import org.springframework.data.aot.sample.ConfigWithQueryMethods.ProjectionInterface;
import org.springframework.data.aot.sample.ConfigWithQuerydslPredicateExecutor;
import org.springframework.data.aot.sample.ConfigWithQuerydslPredicateExecutor.Person;
import org.springframework.data.aot.sample.ConfigWithSimpleCrudRepository;
import org.springframework.data.aot.sample.ConfigWithTransactionManagerPresent;
import org.springframework.data.aot.sample.ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepository;
import org.springframework.data.aot.sample.QConfigWithQuerydslPredicateExecutor_Person;
import org.springframework.data.aot.sample.ReactiveConfig;
import org.springframework.data.domain.AbstractAggregateRoot;
import org.springframework.data.domain.AfterDomainEventPublication;
import org.springframework.data.domain.DomainEvents;
import org.springframework.data.domain.Page;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.aot.RepositoryRegistrationAotProcessorIntegrationTests.EventPublicationConfiguration.Sample;
import org.springframework.data.repository.aot.RepositoryRegistrationAotProcessorIntegrationTests.EventPublicationConfiguration.SampleRepository;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.data.repository.config.RepositoryRegistrationAotContribution;
import org.springframework.data.repository.config.RepositoryRegistrationAotProcessor;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
@@ -292,6 +291,30 @@ public class RepositoryRegistrationAotProcessorIntegrationTests {
});
}
@Test // GH-2939
void registersReflectionForDomainPublicationAnnotations() {
RepositoryRegistrationAotContribution contribution = computeAotConfiguration(EventPublicationConfiguration.class)
.forRepository(SampleRepository.class);
assertThatContribution(contribution).codeContributionSatisfies(it -> {
it.contributesReflectionFor(Sample.class.getDeclaredMethod("publication"));
it.contributesReflectionFor(Sample.class.getDeclaredMethod("cleanup"));
});
}
@Test // GH-2939
void registersReflectionForInheritedDomainPublicationAnnotations() {
RepositoryRegistrationAotContribution contribution = computeAotConfiguration(
InheritedEventPublicationConfiguration.class)
.forRepository(InheritedEventPublicationConfiguration.SampleRepository.class);
assertThatContribution(contribution).codeContributionSatisfies(it -> {
it.contributesReflectionFor(AbstractAggregateRoot.class);
});
}
RepositoryRegistrationAotContributionBuilder computeAotConfiguration(Class<?> configuration) {
return computeAotConfiguration(configuration, new AnnotationConfigApplicationContext());
}
@@ -333,4 +356,31 @@ public class RepositoryRegistrationAotProcessorIntegrationTests {
interface RepositoryRegistrationAotContributionBuilder {
RepositoryRegistrationAotContribution forRepository(Class<?> repositoryInterface);
}
@EnableRepositories(includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = SampleRepository.class) },
considerNestedRepositories = true)
public class EventPublicationConfiguration {
static class Sample {
@DomainEvents
void publication() {}
@AfterDomainEventPublication
void cleanup() {}
}
interface SampleRepository extends Repository<Sample, Object> {}
}
@EnableRepositories(
includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE,
value = InheritedEventPublicationConfiguration.SampleRepository.class) },
considerNestedRepositories = true)
public class InheritedEventPublicationConfiguration {
static class Sample extends AbstractAggregateRoot<Sample> {}
interface SampleRepository extends Repository<Sample, Object> {}
}
}