Introduce infrastructure for AOT processing of repository declarations

This commit introduces initial support for framework 6 bases ahead of time processing of data components and adds extension points for module implementations.

See: #2593
Original Pull Request: #2624
This commit is contained in:
Christoph Strobl
2022-03-28 13:05:43 +02:00
parent 8b560f96c3
commit 21ff2a7e34
72 changed files with 4664 additions and 147 deletions

View File

@@ -0,0 +1,322 @@
/*
* 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 static org.springframework.data.aot.RepositoryBeanContributionAssert.*;
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.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DecoratingProxy;
import org.springframework.core.annotation.SynthesizedAnnotation;
import org.springframework.data.annotation.QueryAnnotation;
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.ConfigWithQueryMethods.ProjectionInterface;
import org.springframework.data.aot.sample.ConfigWithSimpleCrudRepository;
import org.springframework.data.aot.sample.ConfigWithTransactionManagerPresent;
import org.springframework.data.aot.sample.ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty;
import org.springframework.data.aot.sample.ReactiveConfig;
import org.springframework.data.domain.Page;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import org.springframework.transaction.interceptor.TransactionalProxy;
/**
* @author Christoph Strobl
*/
public class AotContributingRepositoryBeanPostProcessorTests {
@Test // GH-2593
void simpleRepositoryNoTxManagerNoKotlinNoReactiveNoComponent() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithSimpleCrudRepository.class)
.forRepository(ConfigWithSimpleCrudRepository.MyRepo.class);
assertThatContribution(repositoryBeanContribution) //
.targetRepositoryTypeIs(ConfigWithSimpleCrudRepository.MyRepo.class) //
.hasNoFragments() //
.codeContributionSatisfies(contribution -> { //
contribution.contributesReflectionFor(ConfigWithSimpleCrudRepository.MyRepo.class) // repository interface
.contributesReflectionFor(PagingAndSortingRepository.class) // base repository
.contributesReflectionFor(ConfigWithSimpleCrudRepository.Person.class) // repository domain type
.contributesJdkProxy(ConfigWithSimpleCrudRepository.MyRepo.class, SpringProxy.class, Advised.class,
DecoratingProxy.class) //
.doesNotContributeJdkProxy(ConfigWithSimpleCrudRepository.MyRepo.class, Repository.class,
TransactionalProxy.class, Advised.class, DecoratingProxy.class)
.doesNotContributeJdkProxy(ConfigWithSimpleCrudRepository.MyRepo.class, Repository.class,
TransactionalProxy.class, Advised.class, DecoratingProxy.class, Serializable.class);
});
}
@Test // GH-2593
void simpleRepositoryWithTxManagerNoKotlinNoReactiveNoComponent() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(
ConfigWithTransactionManagerPresent.class).forRepository(ConfigWithTransactionManagerPresent.MyTxRepo.class);
assertThatContribution(repositoryBeanContribution) //
.targetRepositoryTypeIs(ConfigWithTransactionManagerPresent.MyTxRepo.class) //
.hasNoFragments() //
.codeContributionSatisfies(contribution -> { //
contribution.contributesReflectionFor(ConfigWithTransactionManagerPresent.MyTxRepo.class) // repository
// interface
.contributesReflectionFor(PagingAndSortingRepository.class) // base repository
.contributesReflectionFor(ConfigWithTransactionManagerPresent.Person.class) // repository domain type
// proxies
.contributesJdkProxy(ConfigWithTransactionManagerPresent.MyTxRepo.class, SpringProxy.class, Advised.class,
DecoratingProxy.class)
.contributesJdkProxy(ConfigWithTransactionManagerPresent.MyTxRepo.class, Repository.class,
TransactionalProxy.class, Advised.class, DecoratingProxy.class)
.doesNotContributeJdkProxy(ConfigWithTransactionManagerPresent.MyTxRepo.class, Repository.class,
TransactionalProxy.class, Advised.class, DecoratingProxy.class, Serializable.class);
});
}
@Test // GH-2593
void simpleRepositoryWithTxManagerNoKotlinNoReactiveButComponent() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.class)
.forRepository(ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class);
assertThatContribution(repositoryBeanContribution) //
.targetRepositoryTypeIs(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class) //
.hasNoFragments() //
.codeContributionSatisfies(contribution -> { //
contribution
.contributesReflectionFor(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class) // repository
// interface
.contributesReflectionFor(PagingAndSortingRepository.class) // base repository
.contributesReflectionFor(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.Person.class) // repository domain
// type
// proxies
.contributesJdkProxy(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class,
SpringProxy.class, Advised.class, DecoratingProxy.class)
.contributesJdkProxy(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class,
Repository.class, TransactionalProxy.class, Advised.class, DecoratingProxy.class)
.contributesJdkProxy(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class,
Repository.class, TransactionalProxy.class, Advised.class, DecoratingProxy.class, Serializable.class);
});
}
@Test // GH-2593
void contributesFragmentsCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithFragments.class)
.forRepository(ConfigWithFragments.RepositoryWithFragments.class);
assertThatContribution(repositoryBeanContribution) //
.targetRepositoryTypeIs(ConfigWithFragments.RepositoryWithFragments.class) //
.hasFragments() //
.codeContributionSatisfies(contribution -> { //
contribution.contributesReflectionFor(ConfigWithFragments.RepositoryWithFragments.class) // repository
// interface
.contributesReflectionFor(PagingAndSortingRepository.class) // base repository
.contributesReflectionFor(ConfigWithFragments.Person.class) // repository domain type
// fragments
.contributesReflectionFor(ConfigWithFragments.CustomImplInterface1.class,
ConfigWithFragments.CustomImplInterface1Impl.class)
.contributesReflectionFor(ConfigWithFragments.CustomImplInterface2.class,
ConfigWithFragments.CustomImplInterface2Impl.class)
// proxies
.contributesJdkProxy(ConfigWithFragments.RepositoryWithFragments.class, SpringProxy.class, Advised.class,
DecoratingProxy.class)
.doesNotContributeJdkProxy(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class,
Repository.class, TransactionalProxy.class, Advised.class, DecoratingProxy.class)
.doesNotContributeJdkProxy(
ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo.class,
Repository.class, TransactionalProxy.class, Advised.class, DecoratingProxy.class, Serializable.class);
});
}
@Test // GH-2593
void contributesCustomImplementationCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithCustomImplementation.class)
.forRepository(ConfigWithCustomImplementation.RepositoryWithCustomImplementation.class);
assertThatContribution(repositoryBeanContribution) //
.targetRepositoryTypeIs(ConfigWithCustomImplementation.RepositoryWithCustomImplementation.class) //
.hasFragments() //
.codeContributionSatisfies(contribution -> { //
contribution.contributesReflectionFor(ConfigWithCustomImplementation.RepositoryWithCustomImplementation.class) // repository
// interface
.contributesReflectionFor(PagingAndSortingRepository.class) // base repository
.contributesReflectionFor(ConfigWithCustomImplementation.Person.class) // repository domain type
// fragments
.contributesReflectionFor(ConfigWithCustomImplementation.CustomImplInterface.class,
ConfigWithCustomImplementation.RepositoryWithCustomImplementationImpl.class);
});
}
@Test // GH-2593
void contributesDomainTypeAndReachablesCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(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)
.forRepository(ReactiveConfig.CustomerRepositoryReactive.class);
assertThatContribution(repositoryBeanContribution) //
.targetRepositoryTypeIs(ReactiveConfig.CustomerRepositoryReactive.class) //
.hasNoFragments() //
.codeContributionSatisfies(contribution -> { //
// interface
contribution.contributesReflectionFor(ReactiveConfig.CustomerRepositoryReactive.class) // repository
.contributesReflectionFor(ReactiveSortingRepository.class) // base repo class
.contributesReflectionFor(ReactiveConfig.Person.class); // repository domain type
});
}
@Test // GH-2593
void contributesRepositoryBaseClassCorrectly() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(
ConfigWithCustomRepositoryBaseClass.class)
.forRepository(ConfigWithCustomRepositoryBaseClass.CustomerRepositoryWithCustomBaseRepo.class);
assertThatContribution(repositoryBeanContribution) //
.targetRepositoryTypeIs(ConfigWithCustomRepositoryBaseClass.CustomerRepositoryWithCustomBaseRepo.class) //
.hasNoFragments() //
.codeContributionSatisfies(contribution -> { //
// interface
contribution
.contributesReflectionFor(ConfigWithCustomRepositoryBaseClass.CustomerRepositoryWithCustomBaseRepo.class) // repository
.contributesReflectionFor(ConfigWithCustomRepositoryBaseClass.RepoBaseClass.class) // base repo class
.contributesReflectionFor(ConfigWithCustomRepositoryBaseClass.Person.class); // repository domain type
});
}
@Test // GH-2593
void contributesTypesFromQueryMethods() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithQueryMethods.class)
.forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class);
assertThatContribution(repositoryBeanContribution) //
.codeContributionSatisfies(contribution -> {
contribution.contributesReflectionFor(ProjectionInterface.class);
});
}
@Test // GH-2593
void contributesProxiesForPotentialProjections() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithQueryMethods.class)
.forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class);
assertThatContribution(repositoryBeanContribution) //
.codeContributionSatisfies(contribution -> {
contribution.contributesJdkProxyFor(ProjectionInterface.class);
contribution.doesNotContributeJdkProxyFor(Page.class);
contribution.doesNotContributeJdkProxyFor(ConfigWithQueryMethods.Person.class);
});
}
@Test // GH-2593
void contributesProxiesForDataAnnotations() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithQueryMethods.class)
.forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class);
assertThatContribution(repositoryBeanContribution) //
.codeContributionSatisfies(contribution -> {
contribution.contributesJdkProxy(Param.class, SynthesizedAnnotation.class);
contribution.contributesJdkProxy(ConfigWithQueryMethods.CustomQuery.class, SynthesizedAnnotation.class);
contribution.contributesJdkProxy(QueryAnnotation.class, SynthesizedAnnotation.class);
});
}
@Test // GH-2593
void doesNotCareAboutNonDataAnnotations() {
RepositoryBeanContribution repositoryBeanContribution = computeConfiguration(ConfigWithSimpleCrudRepository.class)
.forRepository(ConfigWithSimpleCrudRepository.MyRepo.class);
assertThatContribution(repositoryBeanContribution) //
.codeContributionSatisfies(contribution -> {
contribution.doesNotContributeReflectionFor(javax.annotation.Nullable.class);
contribution.doesNotContributeJdkProxyFor(javax.annotation.Nullable.class);
});
}
BeanContributionBuilder computeConfiguration(Class<?> configuration, AnnotationConfigApplicationContext ctx) {
ctx.register(configuration);
ctx.refreshForAotProcessing();
return it -> {
String[] repoBeanNames = ctx.getBeanNamesForType(it);
assertThat(repoBeanNames).describedAs("Unable to find repository %s in configuration %s.", it, configuration)
.hasSize(1);
String beanName = repoBeanNames[0];
BeanDefinition beanDefinition = ctx.getBeanDefinition(beanName);
AotContributingRepositoryBeanPostProcessor postProcessor = ctx
.getBean(AotContributingRepositoryBeanPostProcessor.class);
postProcessor.setBeanFactory(ctx.getDefaultListableBeanFactory());
return postProcessor.contribute((RootBeanDefinition) beanDefinition, it, beanName);
};
}
BeanContributionBuilder computeConfiguration(Class<?> configuration) {
return computeConfiguration(configuration, new AnnotationConfigApplicationContext());
}
interface BeanContributionBuilder {
RepositoryBeanContribution forRepository(Class<?> repositoryInterface);
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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 static org.mockito.Mockito.*;
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;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.data.ManagedTypes;
/**
* @author Christoph Strobl
*/
class AotDataComponentsBeanFactoryPostProcessorUnitTests {
@Test // Gh-2593
void replacesManagedTypesBeanDefinitionUsingSupplierForCtorValue() {
Supplier<Iterable<Class<?>>> typesSupplier = mock(Supplier.class);
Mockito.when(typesSupplier.get()).thenReturn(Collections.singleton(DomainType.class));
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("data.managed-types", BeanDefinitionBuilder
.rootBeanDefinition(ManagedTypes.class).addConstructorArgValue(typesSupplier).getBeanDefinition());
new AotDataComponentsBeanFactoryPostProcessor().contribute(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())
.isEqualTo(Collections.singleton(DomainType.class));
}
@Test // Gh-2593
void leavesManagedTypesBeanDefinitionNotUsingSupplierForCtorValue() {
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);
assertThat(beanFactory.getBeanNamesForType(ManagedTypes.class)).hasSize(1);
verifyNoInteractions(types);
assertThat(beanFactory.getBeanDefinition("data.managed-types")).isSameAs(sourceBD);
}
private static class DomainType {}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.Collections;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generator.DefaultCodeContribution;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.generator.BeanInstantiationContribution;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.data.ManagedTypes;
/**
* @author Christoph Strobl
*/
class AotManagedTypesPostProcessorUnitTests {
final RootBeanDefinition managedTypesDefinition = (RootBeanDefinition) BeanDefinitionBuilder
.rootBeanDefinition(ManagedTypes.class).setFactoryMethod("of")
.addConstructorArgValue(Collections.singleton(A.class)).getBeanDefinition();
final RootBeanDefinition myManagedTypesDefinition = (RootBeanDefinition) BeanDefinitionBuilder
.rootBeanDefinition(MyManagedTypes.class).getBeanDefinition();
@Test // GH-2593
void processesBeanWithMatchingModulePrefix() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", managedTypesDefinition);
}).contribute(managedTypesDefinition, ManagedTypes.class, "commons.managed-types");
assertThat(contribution).isNotNull();
}
@Test // GH-2593
void contributesReflectionForManagedTypes() {
BeanInstantiationContribution 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);
new CodeContributionAssert(codeContribution) //
.contributesReflectionFor(A.class) //
.doesNotContributeReflectionFor(B.class);
}
@Test // GH-2593
void processesMatchingSubtypeBean() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", myManagedTypesDefinition);
}).contribute(myManagedTypesDefinition, MyManagedTypes.class, "commons.managed-types");
assertThat(contribution).isNotNull();
}
@Test // GH-2593
void ignoresBeanNotMatchingRequiredType() {
BeanInstantiationContribution contribution = createPostProcessor("commons", bf -> {
bf.registerBeanDefinition("commons.managed-types", managedTypesDefinition);
}).contribute(managedTypesDefinition, Object.class, "commons.managed-types");
assertThat(contribution).isNull();
}
@Test // GH-2593
void ignoresBeanNotMatchingPrefix() {
BeanInstantiationContribution 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) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
action.accept(beanFactory);
AotManagedTypesPostProcessor postProcessor = createPostProcessor(beanFactory);
postProcessor.setModulePrefix(prefix);
return postProcessor;
}
private AotManagedTypesPostProcessor createPostProcessor(BeanFactory beanFactory) {
AotManagedTypesPostProcessor aotManagedTypesPostProcessor = new AotManagedTypesPostProcessor();
aotManagedTypesPostProcessor.setBeanFactory(beanFactory);
return aotManagedTypesPostProcessor;
}
static class A {}
static class B {}
static class MyManagedTypes implements ManagedTypes {
@Override
public void forEach(Consumer<Class<?>> action) {
// just do nothing ¯\_(ツ)_/¯
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.Arrays;
import java.util.List;
import org.assertj.core.api.AbstractAssert;
import org.springframework.aot.hint.ClassProxyHint;
import org.springframework.aot.hint.TypeReference;
/**
* @author Christoph Strobl
* @since 2022/04
*/
public class ClassProxyAssert extends AbstractAssert<ClassProxyAssert, ClassProxyHint> {
protected ClassProxyAssert(ClassProxyHint classProxyHint) {
super(classProxyHint, ClassProxyAssert.class);
}
public void matches(Class<?>... proxyInterfaces) {
assertThat(actual.getProxiedInterfaces().stream().map(TypeReference::getCanonicalName))
.containsExactly(Arrays.stream(proxyInterfaces).map(Class::getCanonicalName).toArray(String[]::new));
}
public List<TypeReference> getProxiedInterfaces() {
return actual.getProxiedInterfaces();
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.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.hint.ClassProxyHint;
import org.springframework.aot.hint.JdkProxyHint;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.javapoet.support.MultiStatement;
/**
* @author Christoph Strobl
* @since 2022/04
*/
public class CodeContributionAssert extends AbstractAssert<CodeContributionAssert, CodeContribution>
implements CodeContribution {
public CodeContributionAssert(CodeContribution 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();
}
return this;
}
public CodeContributionAssert contributesJdkProxyFor(Class<?> entryPoint) {
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(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);
});
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());
});
}
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);
});
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();
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.Arrays;
import java.util.List;
import org.assertj.core.api.AbstractAssert;
import org.springframework.aot.hint.JdkProxyHint;
import org.springframework.aot.hint.TypeReference;
/**
* @author Christoph Strobl
* @since 2022/04
*/
public class JdkProxyAssert extends AbstractAssert<JdkProxyAssert, JdkProxyHint> {
public JdkProxyAssert(JdkProxyHint jdkProxyHint) {
super(jdkProxyHint, JdkProxyAssert.class);
}
public void matches(Class<?>... proxyInterfaces) {
assertThat(actual.getProxiedInterfaces().stream().map(TypeReference::getCanonicalName))
.containsExactly(Arrays.stream(proxyInterfaces).map(Class::getCanonicalName).toArray(String[]::new));
}
public List<TypeReference> getProxiedInterfaces() {
return actual.getProxiedInterfaces();
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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,63 @@
/*
* 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 org.junit.jupiter.api.Test;
import org.springframework.data.aot.types.*;
/**
* @author Christoph Strobl
*/
public class TypeCollectorUnitTests {
@Test // GH-2593
void detectsSignatureTypes() {
assertThat(TypeCollector.inspect(FieldsAndMethods.class).list()).containsExactlyInAnyOrder(FieldsAndMethods.class,
AbstractType.class, InterfaceType.class);
}
@Test // GH-2593
void detectsMethodArgs() {
assertThat(TypeCollector.inspect(TypesInMethodSignatures.class).list())
.containsExactlyInAnyOrder(TypesInMethodSignatures.class, EmptyType1.class, EmptyType2.class);
}
@Test // GH-2593
void doesNotOverflowOnCyclicPropertyReferences() {
assertThat(TypeCollector.inspect(CyclicPropertiesA.class).list()).containsExactlyInAnyOrder(CyclicPropertiesA.class,
CyclicPropertiesB.class);
}
@Test
void doesNotOverflowOnCyclicSelfReferences() {
assertThat(TypeCollector.inspect(CyclicPropertiesSelf.class).list())
.containsExactlyInAnyOrder(CyclicPropertiesSelf.class);
}
@Test
void doesNotOverflowOnCyclicGenericsReferences() {
assertThat(TypeCollector.inspect(CyclicGenerics.class).list()).containsExactlyInAnyOrder(CyclicGenerics.class);
}
@Test
void includesDeclaredClassesInInspection() {
assertThat(TypeCollector.inspect(WithDeclaredClass.class).list()).containsExactlyInAnyOrder(WithDeclaredClass.class,
WithDeclaredClass.SomeEnum.class);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2021 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.sample;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.aot.sample.ConfigWithCustomFactoryBeanBaseClass.MyFixedRepoFactory;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.data.repository.core.support.DummyRepositoryFactoryBean;
/**
* @author Christoph Strobl
*/
@Configuration
@EnableRepositories(repositoryFactoryBeanClass = MyFixedRepoFactory.class, considerNestedRepositories = true,
includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*FixedFactoryRepository") })
public class ConfigWithCustomFactoryBeanBaseClass {
public interface FixedFactoryRepository extends CrudRepository<Person, String> {
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
public static class MyFixedRepoFactory extends DummyRepositoryFactoryBean<FixedFactoryRepository, Person, String> {
public MyFixedRepoFactory(Class<? extends FixedFactoryRepository> repositoryInterface) {
super(FixedFactoryRepository.class);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2019-2021 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.sample;
import java.util.Collections;
import java.util.List;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.stereotype.Component;
/**
* @author Christoph Strobl
*/
@Configuration
@EnableRepositories(considerNestedRepositories = true,
includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*RepositoryWithCustomImplementation") })
public class ConfigWithCustomImplementation {
public interface RepositoryWithCustomImplementation extends Repository<Person, String>, CustomImplInterface {
}
public interface CustomImplInterface {
List<Person> findMyCustomer();
}
@Component
public static class RepositoryWithCustomImplementationImpl implements CustomImplInterface {
@Override
public List<Person> findMyCustomer() {
return Collections.emptyList();
}
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2021 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.sample;
import lombok.experimental.Delegate;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.aot.sample.ConfigWithCustomRepositoryBaseClass.RepoBaseClass;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.config.EnableRepositories;
/**
* @author Christoph Strobl
*/
@Configuration
@EnableRepositories(repositoryBaseClass = RepoBaseClass.class, considerNestedRepositories = true,
includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*CustomerRepositoryWithCustomBaseRepo$") })
public class ConfigWithCustomRepositoryBaseClass {
public interface CustomerRepositoryWithCustomBaseRepo extends CrudRepository<Person, String> {
}
public static class RepoBaseClass<T, ID> implements CrudRepository<T, ID> {
private @Delegate CrudRepository<T, ID> delegate;
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2019-2021 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.sample;
import java.util.Collections;
import java.util.List;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.stereotype.Component;
/**
* @author Christoph Strobl
*/
@Configuration
@EnableRepositories(considerNestedRepositories = true,
includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*RepositoryWithFragments") })
public class ConfigWithFragments {
public interface RepositoryWithFragments
extends Repository<Person, String>, CustomImplInterface1, CustomImplInterface2 {
}
public interface CustomImplInterface1 {
List<Customer> findMyCustomer();
}
@Component
public static class CustomImplInterface1Impl implements CustomImplInterface1 {
@Override
public List<Customer> findMyCustomer() {
return Collections.emptyList();
}
}
public interface CustomImplInterface2 {
}
@Component
public static class CustomImplInterface2Impl implements CustomImplInterface2 {
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
public static class Customer {
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2019-2021 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.sample;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nullable;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.annotation.QueryAnnotation;
import org.springframework.data.domain.Page;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.data.repository.query.Param;
/**
* @author Christoph Strobl
*/
@Configuration
@EnableRepositories(considerNestedRepositories = true, includeFilters = {@Filter(type = FilterType.REGEX, pattern = ".*CustomerRepositoryWithQueryMethods")})
public class ConfigWithQueryMethods {
public interface CustomerRepositoryWithQueryMethods extends Repository<Person, String> {
Page<Person> findAllBy(@Param("longValue") Long val);
@CustomQuery
String customQuery();
ProjectionInterface findProjectionBy();
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
public interface ProjectionInterface {}
@Nullable
@QueryAnnotation
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomQuery {
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.sample;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.aot.sample.ConfigWithSimpleCrudRepository.MyRepo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.config.EnableRepositories;
/**
* @author Christoph Strobl
*/
@EnableRepositories(includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyRepo.class) },
basePackageClasses = ConfigWithSimpleCrudRepository.class, considerNestedRepositories = true)
public class ConfigWithSimpleCrudRepository {
public interface MyRepo extends CrudRepository<Person, String> {
}
public static class Person {
@javax.annotation.Nullable
Address address;
}
public static class Address {
String street;
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.sample;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.aot.sample.ConfigWithTransactionManagerPresent.MyTxRepo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.transaction.TransactionManager;
/**
* @author Christoph Strobl
*/
@EnableRepositories(includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyTxRepo.class) },
basePackageClasses = ConfigWithTransactionManagerPresent.class, considerNestedRepositories = true)
public class ConfigWithTransactionManagerPresent {
public interface MyTxRepo extends CrudRepository<Person, String> {
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
@Bean
TransactionManager txManager() {
return Mockito.mock(TransactionManager.class);
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.sample;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.aot.sample.ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.MyComponentTxRepo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionManager;
/**
* @author Christoph Strobl
*/
@EnableRepositories(includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyComponentTxRepo.class) },
basePackageClasses = ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.class,
considerNestedRepositories = true)
public class ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty {
@Component
public interface MyComponentTxRepo extends CrudRepository<Person, String> {
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
@Bean
TransactionManager txManager() {
return Mockito.mock(TransactionManager.class);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2019-2021 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.sample;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.repository.config.EnableReactiveRepositories;
import org.springframework.data.repository.config.EnableRepositories;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
/**
* @author Christoph Strobl
*/
@Configuration
@EnableReactiveRepositories(considerNestedRepositories = true, includeFilters = {@Filter(type = FilterType.REGEX, pattern = ".*CustomerRepositoryReactive$")})
public class ReactiveConfig {
public interface CustomerRepositoryReactive extends ReactiveCrudRepository<Person, String> {
}
public static class Person {
Address address;
}
public static class Address {
String street;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2021 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.types;
/**
* @author Christoph Strobl
*/
public abstract class AbstractType {
private Object fieldInAbstractType;
abstract Object abstractMethod();
Object methodDefinedInAbstractType() {
return null;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2021 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.types;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.PersistenceCreator;
import org.springframework.data.geo.Point;
/**
* @author Christoph Strobl
*/
public class Address implements LocationHolder {
String street;
Point location;
Address() {
}
@PersistenceCreator
Address(String street) {
this.street = street;
}
@Override
public Point getLocation() {
return location;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2019-2021 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.types;
/**
* @author Christoph Strobl
*/
public class BaseEntity {
Address address;
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2019-2021 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.types;
import java.time.Instant;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.TypeAlias;
/**
* @author Christoph Strobl
*/
@TypeAlias("cu")
public class Customer extends BaseEntity {
@Id
String id;
@Transient
String transientProperty;
@LastModifiedDate
Instant modifiedAt;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2021 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.types;
/**
* @author Christoph Strobl
*/
public class CyclicGenerics<T extends CyclicGenerics<? extends CyclicGenerics<T>>> {
T property;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2019-2021 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.types;
/**
* @author Christoph Strobl
*/
public class CyclicPropertiesA {
CyclicPropertiesB b;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2021 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.types;
/**
* @author Christoph Strobl
*/
public class CyclicPropertiesB {
CyclicPropertiesA refToA;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2021 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.types;
/**
* @author Christoph Strobl
*/
public class CyclicPropertiesSelf {
CyclicPropertiesSelf refSelf;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2021 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.types;
import org.springframework.data.annotation.Id;
/**
* @author Christoph Strobl
*/
public class DomainObjectWithSimpleTypesOnly {
@Id
String id;
Long longValue;
int primValue;
}

View File

@@ -0,0 +1,23 @@
/*
* 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.types;
/**
* @author Christoph Strobl
*/
public class EmptyType1 {
}

View File

@@ -0,0 +1,23 @@
/*
* 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.types;
/**
* @author Christoph Strobl
*/
public class EmptyType2 {
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2019-2021 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.types;
/**
* @author Christoph Strobl
*/
public class FieldsAndMethods extends AbstractType implements InterfaceType {
public static final Object CONSTANT_FIELD = null;
private Object privateField;
Object packagePrivateField;
protected Object protectedField;
public Object publicField;
@Override
Long abstractMethod() {
return null;
}
@Override
public Integer someDefaultMethod() {
return null;
}
private Object privateMethod() {
return null;
}
Object packagePrivateMethod() {
return null;
}
protected Object protectedMethod() {
return null;
}
public Object publicMethod() {
return null;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2021 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.types;
/**
* @author Christoph Strobl
*/
public interface InterfaceType {
default Object someDefaultMethod() {
return null;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2019-2021 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.types;
import org.springframework.data.geo.Point;
/**
* @author Christoph Strobl
*/
public interface LocationHolder {
Point getLocation();
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2021 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.types;
/**
* @author Christoph Strobl
*/
public interface ProjectionInterface {
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2019-2021 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.types;
/**
* @author Christoph Strobl
*/
public class TypesInMethodSignatures {
TypesInMethodSignatures(String ctorArg) {
}
Long returnValue() {
return null;
}
void voidReturn() {
}
EmptyType1 aDomainType() {
return null;
}
void setSomething(EmptyType2 something) {
}
Object methodArg(Integer methodArg) {
return null;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2021 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.types;
/**
* @author Christoph Strobl
*/
public class WithDeclaredClass {
public enum SomeEnum {};
}

View File

@@ -0,0 +1,34 @@
/*
* 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.repository.config;
import org.springframework.data.repository.core.support.DummyRepositoryFactoryBean;
/**
* @author Christoph Strobl
* @since 2022/04
*/
class DummyConfigurationExtension extends RepositoryConfigurationExtensionSupport {
public String getRepositoryFactoryBeanClassName() {
return DummyRepositoryFactoryBean.class.getName();
}
@Override
public String getModulePrefix() {
return "commons";
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.repository.config;
import java.lang.annotation.Annotation;
import org.springframework.core.io.DefaultResourceLoader;
/**
* @author Christoph Strobl
* @since 2022/04
*/
class DummyRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
DummyRegistrar() {
setResourceLoader(new DefaultResourceLoader());
}
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableRepositories.class;
}
@Override
protected RepositoryConfigurationExtension getExtension() {
return new DummyConfigurationExtension();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2012-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.repository.config;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.data.repository.core.support.ReactiveDummyRepositoryFactoryBean;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
@Retention(RetentionPolicy.RUNTIME)
@Import(ReactiveDummyRegistrar.class)
@Inherited
public @interface EnableReactiveRepositories {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
Class<?> repositoryFactoryBeanClass() default ReactiveDummyRepositoryFactoryBean.class;
Class<?> repositoryBaseClass() default ReactiveSortingRepository.class;
String namedQueriesLocation() default "";
String repositoryImplementationPostfix() default "Impl";
boolean considerNestedRepositories() default false;
boolean limitImplementationBasePackages() default true;
BootstrapMode bootstrapMode() default BootstrapMode.DEFAULT;
}

View File

@@ -22,7 +22,6 @@ import java.lang.annotation.RetentionPolicy;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupportUnitTests.DummyRegistrar;
import org.springframework.data.repository.core.support.DummyRepositoryFactoryBean;
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -0,0 +1,43 @@
/*
* 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.repository.config;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.ReactiveDummyRepositoryFactoryBean;
/**
* @author Christoph Strobl
* @since 2022/04
*/
class ReactiveDummyConfigurationExtension extends RepositoryConfigurationExtensionSupport {
public String getRepositoryFactoryBeanClassName() {
return ReactiveDummyRepositoryFactoryBean.class.getName();
}
@Override
public String getModulePrefix() {
return "commons";
}
@Override
protected boolean useRepositoryConfiguration(RepositoryMetadata metadata) {
if(metadata.isReactiveRepository()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.repository.config;
import java.lang.annotation.Annotation;
import org.springframework.core.io.DefaultResourceLoader;
/**
* @author Christoph Strobl
* @since 2022/04
*/
class ReactiveDummyRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
ReactiveDummyRegistrar() {
setResourceLoader(new DefaultResourceLoader());
}
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableReactiveRepositories.class;
}
@Override
protected RepositoryConfigurationExtension getExtension() {
return new ReactiveDummyConfigurationExtension();
}
}

View File

@@ -25,7 +25,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupportUnitTests.DummyConfigurationExtension;
import org.springframework.util.ClassUtils;
/**

View File

@@ -18,8 +18,6 @@ package org.springframework.data.repository.config;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.lang.annotation.Annotation;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -33,7 +31,6 @@ import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.mapping.Person;

View File

@@ -110,7 +110,7 @@ class RepositoryConfigurationExtensionSupportUnitTests {
}
@Override
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
public Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
return Collections.singleton(Primary.class);
}

View File

@@ -19,8 +19,10 @@ import static org.assertj.core.api.Assertions.*;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;

View File

@@ -16,6 +16,7 @@
package org.springframework.data.repository.core.support;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
import org.springframework.data.repository.core.CrudMethods;
@@ -103,4 +104,9 @@ public final class DummyRepositoryInformation implements RepositoryInformation {
public boolean isReactiveRepository() {
return metadata.isReactiveRepository();
}
@Override
public Set<RepositoryFragment<?>> getFragments() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2012-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.repository.core.support;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.function.Supplier;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.metrics.StartupStep;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
/**
* Dummy implementation for {@link RepositoryFactorySupport} that is equipped with mocks to simulate behavior for test
* cases.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class ReactiveDummyRepositoryFactory extends ReactiveRepositoryFactorySupport {
public final MyRepositoryQuery queryOne = mock(MyRepositoryQuery.class);
public final RepositoryQuery queryTwo = mock(RepositoryQuery.class);
public final QueryLookupStrategy strategy = mock(QueryLookupStrategy.class);
private final ApplicationStartup applicationStartup;
@SuppressWarnings("unchecked") private final QuerydslPredicateExecutor<Object> querydsl = mock(
QuerydslPredicateExecutor.class);
private final Object repository;
public ReactiveDummyRepositoryFactory(Object repository) {
this.repository = repository;
when(strategy.resolveQuery(Mockito.any(Method.class), Mockito.any(RepositoryMetadata.class),
Mockito.any(ProjectionFactory.class), Mockito.any(NamedQueries.class))).thenReturn(queryOne);
this.applicationStartup = mock(ApplicationStartup.class);
var startupStep = mock(StartupStep.class);
when(applicationStartup.start(anyString())).thenReturn(startupStep);
when(startupStep.tag(anyString(), anyString())).thenReturn(startupStep);
when(startupStep.tag(anyString(), ArgumentMatchers.<Supplier<String>> any())).thenReturn(startupStep);
var beanFactory = Mockito.mock(BeanFactory.class);
when(beanFactory.getBean(ApplicationStartup.class)).thenReturn(applicationStartup);
setBeanFactory(beanFactory);
}
@Override
@SuppressWarnings("unchecked")
public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
return mock(EntityInformation.class);
}
@Override
protected Object getTargetRepository(RepositoryInformation information) {
return repository;
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return repository.getClass();
}
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of(strategy);
}
@Override
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
var fragments = super.getRepositoryFragments(metadata);
return QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface()) //
? fragments.append(RepositoryFragments.just(querydsl)) //
: fragments;
}
ApplicationStartup getApplicationStartup() {
return this.applicationStartup;
}
/**
* @author Mark Paluch
*/
public interface MyRepositoryQuery extends RepositoryQuery {
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012-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.repository.core.support;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import org.springframework.data.mapping.context.SampleMappingContext;
import org.springframework.data.repository.Repository;
/**
* @author Oliver Gierke
*/
public class ReactiveDummyRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
extends RepositoryFactoryBeanSupport<T, S, ID> {
private final T repository;
public ReactiveDummyRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
this.repository = mock(repositoryInterface);
setMappingContext(new SampleMappingContext());
}
@Override
protected RepositoryFactorySupport createRepositoryFactory() {
return new ReactiveDummyRepositoryFactory(repository);
}
}