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:
committed by
Christoph Strobl
parent
21ff2a7e34
commit
d8118de90f
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user