Review, refactor and polish Spring Data AOT infrastructure classes.

* Refactored logic in AOT infrastructure classes.
* Annotated AOT API with Spring's @NonNull and @Nullable annotations.
* Edited Javadoc.
* Introduced PredicateUtils abstract utility class encapsulating common Predicates on types and class members.
* Added comments for review and clarification.

Original Pull Request: #2624
This commit is contained in:
John Blum
2022-05-05 16:26:15 -07:00
committed by Christoph Strobl
parent 21ff2a7e34
commit d8118de90f
30 changed files with 1813 additions and 1057 deletions

View File

@@ -15,106 +15,112 @@
*/
package org.springframework.data.aot;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.stream.Stream;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.Assertions;
import org.springframework.aot.generator.CodeContribution;
import org.springframework.aot.generator.ProtectedAccess;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.ClassProxyHint;
import org.springframework.aot.hint.JdkProxyHint;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.javapoet.support.MultiStatement;
/**
* AssertJ {@link AbstractAssert Assertion} for code contributions originating from
* Spring Data Repository infrastructure AOT processing.
*
* @author Christoph Strobl
* @since 2022/04
* @author John Blum
* @see org.assertj.core.api.AbstractAssert
* @see org.springframework.aot.generate.GenerationContext
* @since 3.0.0
*/
public class CodeContributionAssert extends AbstractAssert<CodeContributionAssert, CodeContribution>
implements CodeContribution {
public class CodeContributionAssert extends AbstractAssert<CodeContributionAssert, GenerationContext> {
public CodeContributionAssert(CodeContribution contribution) {
public CodeContributionAssert(GenerationContext contribution) {
super(contribution, CodeContributionAssert.class);
}
public CodeContributionAssert doesNotContributeReflectionFor(Class<?>... types) {
for (Class<?> type : types) {
assertThat(this.actual.runtimeHints().reflection().getTypeHint(type))
.describedAs("Reflection entry found for %s", type).isNull();
}
return this;
}
public CodeContributionAssert contributesReflectionFor(Class<?>... types) {
for (Class<?> type : types) {
assertThat(this.actual.runtimeHints().reflection().getTypeHint(type))
.describedAs("No reflection entry found for %s", type).isNotNull();
assertThat(this.actual.getRuntimeHints().reflection().getTypeHint(type))
.describedAs("No reflection entry found for [%s]", type)
.isNotNull();
}
return this;
}
public CodeContributionAssert doesNotContributeReflectionFor(Class<?>... types) {
for (Class<?> type : types) {
assertThat(this.actual.getRuntimeHints().reflection().getTypeHint(type))
.describedAs("Reflection entry found for [%s]", type)
.isNull();
}
return this;
}
public CodeContributionAssert contributesJdkProxyFor(Class<?> entryPoint) {
assertThat(jdkProxiesFor(entryPoint).findFirst()).describedAs("No jdk proxy found for %s", entryPoint).isPresent();
assertThat(jdkProxiesFor(entryPoint).findFirst())
.describedAs("No JDK proxy found for [%s]", entryPoint)
.isPresent();
return this;
}
public CodeContributionAssert doesNotContributeJdkProxyFor(Class<?> entryPoint) {
assertThat(jdkProxiesFor(entryPoint).findFirst()).describedAs("Found jdk proxy matching %s though it should not be present.", entryPoint).isNotPresent();
return this;
}
public CodeContributionAssert doesNotContributeJdkProxy(Class<?>... proxyInterfaces) {
assertThat(jdkProxiesFor(entryPoint).findFirst())
.describedAs("Found JDK proxy matching [%s] though it should not be present", entryPoint)
.isNotPresent();
assertThat(jdkProxiesFor(proxyInterfaces[0])).describedAs("Found jdk proxy matching %s though it should not be present.", Arrays.asList(proxyInterfaces)).noneSatisfy(it -> {
new JdkProxyAssert(it).matches(proxyInterfaces);
});
return this;
}
public CodeContributionAssert contributesJdkProxy(Class<?>... proxyInterfaces) {
assertThat(jdkProxiesFor(proxyInterfaces[0])).describedAs("Unable to find jdk proxy matching %s", Arrays.asList(proxyInterfaces)).anySatisfy(it -> {
new JdkProxyAssert(it).matches(proxyInterfaces);
});
assertThat(jdkProxiesFor(proxyInterfaces[0]))
.describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces))
.anySatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces));
return this;
}
public CodeContributionAssert doesNotContributeJdkProxy(Class<?>... proxyInterfaces) {
assertThat(jdkProxiesFor(proxyInterfaces[0]))
.describedAs("Found JDK proxy matching [%s] though it should not be present",
Arrays.asList(proxyInterfaces))
.noneSatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces));
return this;
}
private Stream<JdkProxyHint> jdkProxiesFor(Class<?> entryPoint) {
return this.actual.runtimeHints().proxies().jdkProxies().filter(jdkProxyHint -> {
return jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName().equals(entryPoint.getCanonicalName());
});
return this.actual.getRuntimeHints().proxies().jdkProxies()
.filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName()
.equals(entryPoint.getCanonicalName()));
}
public CodeContributionAssert contributesClassProxy(Class<?>... proxyInterfaces) {
assertThat(classProxiesFor(proxyInterfaces[0])).describedAs("Unable to find jdk proxy matching %s", Arrays.asList(proxyInterfaces)).anySatisfy(it -> {
new ClassProxyAssert(it).matches(proxyInterfaces);
});
assertThat(classProxiesFor(proxyInterfaces[0]))
.describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces))
.anySatisfy(it -> new ClassProxyAssert(it).matches(proxyInterfaces));
return this;
}
private Stream<ClassProxyHint> classProxiesFor(Class<?> entryPoint) {
return this.actual.runtimeHints().proxies().classProxies().filter(jdkProxyHint -> {
return jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName().equals(entryPoint.getCanonicalName());
});
}
public MultiStatement statements() {
return actual.statements();
}
public RuntimeHints runtimeHints() {
return actual.runtimeHints();
}
public ProtectedAccess protectedAccess() {
return actual.protectedAccess();
return this.actual.getRuntimeHints().proxies().classProxies()
.filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName()
.equals(entryPoint.getCanonicalName()));
}
}

View File

@@ -21,10 +21,13 @@ import java.util.Collections;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generator.DefaultCodeContribution;
import org.springframework.aot.generate.ClassNameGenerator;
import org.springframework.aot.generate.DefaultGenerationContext;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.generator.BeanInstantiationContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -33,7 +36,7 @@ import org.springframework.data.ManagedTypes;
/**
* @author Christoph Strobl
*/
class AotManagedTypesPostProcessorUnitTests {
class ManagedTypesRegistrationAotProcessorUnitTests {
final RootBeanDefinition managedTypesDefinition = (RootBeanDefinition) BeanDefinitionBuilder
.rootBeanDefinition(ManagedTypes.class).setFactoryMethod("of")
@@ -45,7 +48,7 @@ class AotManagedTypesPostProcessorUnitTests {
@Test // GH-2593
void processesBeanWithMatchingModulePrefix() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
BeanRegistrationAotContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", managedTypesDefinition);
}).contribute(managedTypesDefinition, ManagedTypes.class, "commons.managed-types");
@@ -55,14 +58,16 @@ class AotManagedTypesPostProcessorUnitTests {
@Test // GH-2593
void contributesReflectionForManagedTypes() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
BeanRegistrationAotContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", managedTypesDefinition);
}).contribute(managedTypesDefinition, ManagedTypes.class, "commons.managed-types");
DefaultCodeContribution codeContribution = new DefaultCodeContribution(new RuntimeHints());
contribution.applyTo(codeContribution);
DefaultGenerationContext generationContext = new DefaultGenerationContext(new ClassNameGenerator(),
new InMemoryGeneratedFiles(), new RuntimeHints());
new CodeContributionAssert(codeContribution) //
contribution.applyTo(generationContext, null);
new CodeContributionAssert(generationContext) //
.contributesReflectionFor(A.class) //
.doesNotContributeReflectionFor(B.class);
}
@@ -70,7 +75,7 @@ class AotManagedTypesPostProcessorUnitTests {
@Test // GH-2593
void processesMatchingSubtypeBean() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
BeanRegistrationAotContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", myManagedTypesDefinition);
}).contribute(myManagedTypesDefinition, MyManagedTypes.class, "commons.managed-types");
@@ -80,7 +85,7 @@ class AotManagedTypesPostProcessorUnitTests {
@Test // GH-2593
void ignoresBeanNotMatchingRequiredType() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
BeanRegistrationAotContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", managedTypesDefinition);
}).contribute(managedTypesDefinition, Object.class, "commons.managed-types");
@@ -90,29 +95,29 @@ class AotManagedTypesPostProcessorUnitTests {
@Test // GH-2593
void ignoresBeanNotMatchingPrefix() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
BeanRegistrationAotContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", managedTypesDefinition);
}).contribute(managedTypesDefinition, ManagedTypes.class, "jpa.managed-types");
assertThat(contribution).isNull();
}
private AotManagedTypesPostProcessor createPostProcessor(String prefix, Consumer<DefaultListableBeanFactory> action) {
private ManagedTypesRegistrationAotProcessor createPostProcessor(String prefix, Consumer<DefaultListableBeanFactory> action) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
action.accept(beanFactory);
AotManagedTypesPostProcessor postProcessor = createPostProcessor(beanFactory);
ManagedTypesRegistrationAotProcessor postProcessor = createPostProcessor(beanFactory);
postProcessor.setModulePrefix(prefix);
return postProcessor;
}
private AotManagedTypesPostProcessor createPostProcessor(BeanFactory beanFactory) {
private ManagedTypesRegistrationAotProcessor createPostProcessor(BeanFactory beanFactory) {
AotManagedTypesPostProcessor aotManagedTypesPostProcessor = new AotManagedTypesPostProcessor();
aotManagedTypesPostProcessor.setBeanFactory(beanFactory);
return aotManagedTypesPostProcessor;
ManagedTypesRegistrationAotProcessor managedTypesRegistrationAotProcessor = new ManagedTypesRegistrationAotProcessor();
managedTypesRegistrationAotProcessor.setBeanFactory(beanFactory);
return managedTypesRegistrationAotProcessor;
}
static class A {}

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2022 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.*;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
import org.assertj.core.api.AbstractAssert;
import org.springframework.aot.generator.CodeContribution;
import org.springframework.aot.generator.DefaultCodeContribution;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.RepositoryFragment;
/**
* @author Christoph Strobl
* @since 2022/04
*/
public class RepositoryBeanContributionAssert
extends AbstractAssert<RepositoryBeanContributionAssert, RepositoryBeanContribution> {
public RepositoryBeanContributionAssert(RepositoryBeanContribution actual) {
super(actual, RepositoryBeanContributionAssert.class);
}
public static RepositoryBeanContributionAssert assertThatContribution(RepositoryBeanContribution actual) {
return new RepositoryBeanContributionAssert(actual);
}
public RepositoryBeanContributionAssert targetRepositoryTypeIs(Class<?> expected) {
assertThat(getRepositoryInformation().getRepositoryInterface()).isEqualTo(expected);
return myself;
}
public RepositoryBeanContributionAssert hasNoFragments() {
assertThat(getRepositoryInformation().getFragments()).isEmpty();
return this;
}
public RepositoryBeanContributionAssert hasFragments() {
assertThat(getRepositoryInformation().getFragments()).isNotEmpty();
return this;
}
public RepositoryBeanContributionAssert verifyFragments(Consumer<Set<RepositoryFragment<?>>> consumer) {
assertThat(getRepositoryInformation().getFragments()).satisfies(it -> consumer.accept(new LinkedHashSet<>(it)));
return this;
}
public RepositoryBeanContributionAssert codeContributionSatisfies(Consumer<CodeContributionAssert> consumer) {
DefaultCodeContribution codeContribution = new DefaultCodeContribution(new RuntimeHints());
this.actual.applyTo(codeContribution);
consumer.accept(new CodeContributionAssert(codeContribution));
return this;
}
private RepositoryInformation getRepositoryInformation() {
assertThat(this.actual).describedAs("No repository interface found on null bean contribution.").isNotNull();
assertThat(this.actual.getRepositoryInformation())
.describedAs("No repository interface found on null repository information.").isNotNull();
return this.actual.getRepositoryInformation();
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2022 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.mockito.Mockito.mock;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
import org.assertj.core.api.AbstractAssert;
import org.springframework.aot.generate.ClassNameGenerator;
import org.springframework.aot.generate.DefaultGenerationContext;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.aot.BeanRegistrationCode;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.RepositoryFragment;
import org.springframework.lang.NonNull;
/**
* AssertJ {@link AbstractAssert Assertion} for {@link RepositoryRegistrationAotContribution}.
*
* @author Christoph Strobl
* @author John Blum
* @see org.mockito.Mockito
* @see org.assertj.core.api.AbstractAssert
* @see org.springframework.data.aot.RepositoryRegistrationAotContribution
* @since 3.0.0
*/
public class RepositoryRegistrationAotContributionAssert
extends AbstractAssert<RepositoryRegistrationAotContributionAssert, RepositoryRegistrationAotContribution> {
@NonNull
public static RepositoryRegistrationAotContributionAssert assertThatContribution(
@NonNull RepositoryRegistrationAotContribution actual) {
return new RepositoryRegistrationAotContributionAssert(actual);
}
public RepositoryRegistrationAotContributionAssert(@NonNull RepositoryRegistrationAotContribution actual) {
super(actual, RepositoryRegistrationAotContributionAssert.class);
}
public RepositoryRegistrationAotContributionAssert targetRepositoryTypeIs(Class<?> expected) {
assertThat(getRepositoryInformation().getRepositoryInterface()).isEqualTo(expected);
return this.myself;
}
public RepositoryRegistrationAotContributionAssert hasNoFragments() {
assertThat(getRepositoryInformation().getFragments()).isEmpty();
return this;
}
public RepositoryRegistrationAotContributionAssert hasFragments() {
assertThat(getRepositoryInformation().getFragments()).isNotEmpty();
return this;
}
public RepositoryRegistrationAotContributionAssert verifyFragments(Consumer<Set<RepositoryFragment<?>>> consumer) {
assertThat(getRepositoryInformation().getFragments())
.satisfies(it -> consumer.accept(new LinkedHashSet<>(it)));
return this;
}
public RepositoryRegistrationAotContributionAssert codeContributionSatisfies(
Consumer<CodeContributionAssert> assertWith) {
BeanRegistrationCode mockBeanRegistrationCode = mock(BeanRegistrationCode.class);
DefaultGenerationContext generationContext =
new DefaultGenerationContext(new ClassNameGenerator(), new InMemoryGeneratedFiles(), new RuntimeHints());
this.actual.applyTo(generationContext, mockBeanRegistrationCode);
assertWith.accept(new CodeContributionAssert(generationContext));
return this;
}
private RepositoryInformation getRepositoryInformation() {
assertThat(this.actual)
.describedAs("No repository interface found on null bean contribution")
.isNotNull();
assertThat(this.actual.getRepositoryInformation())
.describedAs("No repository interface found on null repository information")
.isNotNull();
return this.actual.getRepositoryInformation();
}
}

View File

@@ -15,16 +15,18 @@
*/
package org.springframework.data.aot;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.aot.RepositoryBeanContributionAssert.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.aot.RepositoryRegistrationAotContributionAssert.assertThatContribution;
import java.io.Serializable;
import org.junit.jupiter.api.Test;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DecoratingProxy;
import org.springframework.core.annotation.SynthesizedAnnotation;
@@ -46,14 +48,20 @@ import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import org.springframework.transaction.interceptor.TransactionalProxy;
/**
* Integration Tests for {@link RepositoryRegistrationAotProcessor}.
*
* @author Christoph Strobl
* @see org.junit.jupiter.api.Test
* @see org.springframework.data.aot.RepositoryRegistrationAotProcessor
* @see org.springframework.data.aot.RepositoryRegistrationAotContributionAssert
* @author John Blum
*/
public class AotContributingRepositoryBeanPostProcessorTests {
public class RepositoryRegistrationAotProcessorIntegrationTests {
@Test // GH-2593
void simpleRepositoryNoTxManagerNoKotlinNoReactiveNoComponent() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithSimpleCrudRepository.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithSimpleCrudRepository.class)
.forRepository(ConfigWithSimpleCrudRepository.MyRepo.class);
assertThatContribution(repositoryBeanContribution) //
@@ -75,7 +83,7 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void simpleRepositoryWithTxManagerNoKotlinNoReactiveNoComponent() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(
ConfigWithTransactionManagerPresent.class).forRepository(ConfigWithTransactionManagerPresent.MyTxRepo.class);
assertThatContribution(repositoryBeanContribution) //
@@ -100,7 +108,7 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void simpleRepositoryWithTxManagerNoKotlinNoReactiveButComponent() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.class)
.forRepository(ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class);
@@ -134,7 +142,7 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void contributesFragmentsCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithFragments.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithFragments.class)
.forRepository(ConfigWithFragments.RepositoryWithFragments.class);
assertThatContribution(repositoryBeanContribution) //
@@ -167,7 +175,7 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void contributesCustomImplementationCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithCustomImplementation.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithCustomImplementation.class)
.forRepository(ConfigWithCustomImplementation.RepositoryWithCustomImplementation.class);
assertThatContribution(repositoryBeanContribution) //
@@ -187,21 +195,21 @@ public class AotContributingRepositoryBeanPostProcessorTests {
}
@Test // GH-2593
void contributesDomainTypeAndReachablesCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithSimpleCrudRepository.class)
.forRepository(ConfigWithSimpleCrudRepository.MyRepo.class);
void contributesDomainTypeAndReachableTypesCorrectly() {
assertThatContribution(repositoryBeanContribution) //
.codeContributionSatisfies(contribution -> {
contribution.contributesReflectionFor(ConfigWithSimpleCrudRepository.Person.class,
ConfigWithSimpleCrudRepository.Address.class);
});
RepositoryRegistrationAotContribution repositoryBeanContribution =
computeAotConfiguration(ConfigWithSimpleCrudRepository.class)
.forRepository(ConfigWithSimpleCrudRepository.MyRepo.class);
assertThatContribution(repositoryBeanContribution).codeContributionSatisfies(contribution ->
contribution.contributesReflectionFor(ConfigWithSimpleCrudRepository.Person.class,
ConfigWithSimpleCrudRepository.Address.class));
}
@Test // GH-2593
void contributesReactiveRepositoryCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ReactiveConfig.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ReactiveConfig.class)
.forRepository(ReactiveConfig.CustomerRepositoryReactive.class);
assertThatContribution(repositoryBeanContribution) //
@@ -218,7 +226,7 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void contributesRepositoryBaseClassCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(
ConfigWithCustomRepositoryBaseClass.class)
.forRepository(ConfigWithCustomRepositoryBaseClass.CustomerRepositoryWithCustomBaseRepo.class);
@@ -237,19 +245,18 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void contributesTypesFromQueryMethods() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithQueryMethods.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithQueryMethods.class)
.forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class);
assertThatContribution(repositoryBeanContribution) //
.codeContributionSatisfies(contribution -> {
contribution.contributesReflectionFor(ProjectionInterface.class);
});
assertThatContribution(repositoryBeanContribution)
.codeContributionSatisfies(contribution ->
contribution.contributesReflectionFor(ProjectionInterface.class));
}
@Test // GH-2593
void contributesProxiesForPotentialProjections() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithQueryMethods.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithQueryMethods.class)
.forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class);
assertThatContribution(repositoryBeanContribution) //
@@ -264,7 +271,7 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void contributesProxiesForDataAnnotations() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithQueryMethods.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithQueryMethods.class)
.forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class);
assertThatContribution(repositoryBeanContribution) //
@@ -279,7 +286,7 @@ public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void doesNotCareAboutNonDataAnnotations() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithSimpleCrudRepository.class)
RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithSimpleCrudRepository.class)
.forRepository(ConfigWithSimpleCrudRepository.MyRepo.class);
assertThatContribution(repositoryBeanContribution) //
@@ -289,34 +296,46 @@ public class AotContributingRepositoryBeanPostProcessorTests {
});
}
BeanContributionBuilder computeConfiguration(Class<?> configuration, AnnotationConfigApplicationContext ctx) {
RepositoryRegistrationAotContributionBuilder computeAotConfiguration(Class<?> configuration) {
return computeAotConfiguration(configuration, new AnnotationConfigApplicationContext());
}
ctx.register(configuration);
ctx.refreshForAotProcessing();
RepositoryRegistrationAotContributionBuilder computeAotConfiguration(Class<?> configuration,
AnnotationConfigApplicationContext applicationContext) {
return it -> {
applicationContext.register(configuration);
applicationContext.refreshForAotProcessing();
String[] repoBeanNames = ctx.getBeanNamesForType(it);
assertThat(repoBeanNames).describedAs("Unable to find repository %s in configuration %s.", it, configuration)
return repositoryType -> {
String[] repositoryBeanNames = applicationContext.getBeanNamesForType(repositoryType);
assertThat(repositoryBeanNames)
.describedAs("Unable to find repository [%s] in configuration [%s]",
repositoryType, configuration)
.hasSize(1);
String beanName = repoBeanNames[0];
BeanDefinition beanDefinition = ctx.getBeanDefinition(beanName);
String repositoryBeanName = repositoryBeanNames[0];
AotContributingRepositoryBeanPostProcessor postProcessor = ctx
.getBean(AotContributingRepositoryBeanPostProcessor.class);
ConfigurableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
postProcessor.setBeanFactory(ctx.getDefaultListableBeanFactory());
RepositoryRegistrationAotProcessor repositoryAotProcessor =
applicationContext.getBean(RepositoryRegistrationAotProcessor.class);
return postProcessor.contribute((RootBeanDefinition) beanDefinition, it, beanName);
repositoryAotProcessor.setBeanFactory(beanFactory);
RegisteredBean bean = RegisteredBean.of(beanFactory, repositoryBeanName);
BeanRegistrationAotContribution beanContribution = repositoryAotProcessor.processAheadOfTime(bean);
assertThat(beanContribution).isInstanceOf(RepositoryRegistrationAotContribution.class);
return (RepositoryRegistrationAotContribution) beanContribution;
};
}
BeanContributionBuilder computeConfiguration(Class<?> configuration) {
return computeConfiguration(configuration, new AnnotationConfigApplicationContext());
}
interface BeanContributionBuilder {
RepositoryBeanContribution forRepository(Class<?> repositoryInterface);
@FunctionalInterface
interface RepositoryRegistrationAotContributionBuilder {
RepositoryRegistrationAotContribution forRepository(Class<?> repositoryInterface);
}
}

View File

@@ -15,15 +15,18 @@
*/
package org.springframework.data.aot;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -33,25 +36,28 @@ import org.springframework.data.ManagedTypes;
/**
* @author Christoph Strobl
*/
class AotDataComponentsBeanFactoryPostProcessorUnitTests {
class SpringDataBeanFactoryInitializationAotProcessorUnitTests {
@Test // Gh-2593
void replacesManagedTypesBeanDefinitionUsingSupplierForCtorValue() {
@SuppressWarnings("all")
void replacesManagedTypesBeanDefinitionUsingSupplierForConstructorValue() {
Supplier<Iterable<Class<?>>> typesSupplier = mock(Supplier.class);
Mockito.when(typesSupplier.get()).thenReturn(Collections.singleton(DomainType.class));
doReturn(Collections.singleton(DomainType.class)).when(typesSupplier).get();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("data.managed-types", BeanDefinitionBuilder
.rootBeanDefinition(ManagedTypes.class).addConstructorArgValue(typesSupplier).getBeanDefinition());
new AotDataComponentsBeanFactoryPostProcessor().contribute(beanFactory);
new SpringDataBeanFactoryInitializationAotProcessor().processAheadOfTime(beanFactory);
assertThat(beanFactory.getBeanNamesForType(ManagedTypes.class)).hasSize(1);
verify(typesSupplier).get();
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("data.managed-types");
assertThat(beanDefinition.getFactoryMethodName()).isEqualTo("of");
assertThat(beanDefinition.hasConstructorArgumentValues()).isTrue();
assertThat(beanDefinition.getConstructorArgumentValues().getArgumentValue(0, null).getValue())
@@ -59,21 +65,23 @@ class AotDataComponentsBeanFactoryPostProcessorUnitTests {
}
@Test // Gh-2593
void leavesManagedTypesBeanDefinitionNotUsingSupplierForCtorValue() {
void leavesManagedTypesBeanDefinitionNotUsingSupplierForConstructorValue() {
Iterable<Class<?>> types = spy(new LinkedHashSet<>(Collections.singleton(DomainType.class)));
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition sourceBD = BeanDefinitionBuilder.rootBeanDefinition(ManagedTypes.class)
.addConstructorArgValue(types).getBeanDefinition();
beanFactory.registerBeanDefinition("data.managed-types", sourceBD);
new AotDataComponentsBeanFactoryPostProcessor().contribute(beanFactory);
AbstractBeanDefinition sourceBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ManagedTypes.class)
.addConstructorArgValue(types).getBeanDefinition();
beanFactory.registerBeanDefinition("data.managed-types", sourceBeanDefinition);
new SpringDataBeanFactoryInitializationAotProcessor().processAheadOfTime(beanFactory);
assertThat(beanFactory.getBeanNamesForType(ManagedTypes.class)).hasSize(1);
verifyNoInteractions(types);
assertThat(beanFactory.getBeanDefinition("data.managed-types")).isSameAs(sourceBD);
assertThat(beanFactory.getBeanDefinition("data.managed-types")).isSameAs(sourceBeanDefinition);
}
private static class DomainType {}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.aot.types;
/**
* @author Christoph Strobl
*/
@SuppressWarnings("unused")
public class TypesInMethodSignatures {
TypesInMethodSignatures(String ctorArg) {
@@ -40,8 +41,6 @@ public class TypesInMethodSignatures {
}
Object methodArg(Integer methodArg) {
return null;
}