diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributor.java index 4222f9827..accca6e74 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributor.java @@ -68,11 +68,11 @@ public class MongoRepositoryContributor extends RepositoryContributor { private static final Log logger = LogFactory.getLog(MongoRepositoryContributor.class); + private final AotRepositoryContext repositoryContext; private final AotQueryCreator queryCreator; private final SimpleTypeHolder simpleTypeHolder; private final MongoMappingContext mappingContext; private final NamedQueries namedQueries; - private final @Nullable String mongoOperationsRef; public MongoRepositoryContributor(AotRepositoryContext repositoryContext) { @@ -83,8 +83,8 @@ public class MongoRepositoryContributor extends RepositoryContributor { classLoader = getClass().getClassLoader(); } + this.repositoryContext = repositoryContext; this.namedQueries = getNamedQueries(repositoryContext.getConfigurationSource(), classLoader); - this.mongoOperationsRef = getMongoTemplateRef(repositoryContext.getConfigurationSource()); // avoid Java Time (JSR-310) Type introspection MongoCustomConversions mongoCustomConversions = MongoCustomConversions @@ -100,14 +100,6 @@ public class MongoRepositoryContributor extends RepositoryContributor { this.queryCreator = new AotQueryCreator(this.mappingContext); } - private @Nullable String getMongoTemplateRef(@Nullable RepositoryConfigurationSource configSource) { - if (configSource == null) { - return null; - } - - return configSource.getAttribute("mongoTemplateRef").filter(it -> !"mongoTemplate".equals(it)).orElse(null); - } - @SuppressWarnings("NullAway") private NamedQueries getNamedQueries(@Nullable RepositoryConfigurationSource configSource, ClassLoader classLoader) { @@ -145,9 +137,10 @@ public class MongoRepositoryContributor extends RepositoryContributor { constructorBuilder.addParameter("operations", MongoOperations.class, customizer -> { + String mongoOperationsRef = getMongoTemplateRef(); customizer.bindToField() - .origin(StringUtils.hasText(this.mongoOperationsRef) - ? new RuntimeBeanReference(this.mongoOperationsRef, MongoOperations.class) + .origin(StringUtils.hasText(mongoOperationsRef) + ? new RuntimeBeanReference(mongoOperationsRef, MongoOperations.class) : new RuntimeBeanReference(MongoOperations.class)); }); @@ -158,6 +151,11 @@ public class MongoRepositoryContributor extends RepositoryContributor { }); } + private @Nullable String getMongoTemplateRef() { + return repositoryContext.getConfigurationSource().getAttribute("mongoTemplateRef") + .filter(it -> !"mongoTemplate".equals(it)).orElse(null); + } + @Override @SuppressWarnings("NullAway") protected @Nullable MethodContributor contributeQueryMethod(Method method) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/AotFragmentTestConfigurationSupport.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/AotFragmentTestConfigurationSupport.java index 61dbe8e71..78e3a3028 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/AotFragmentTestConfigurationSupport.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/AotFragmentTestConfigurationSupport.java @@ -18,6 +18,8 @@ package org.springframework.data.mongodb.repository.aot; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import org.mockito.Mockito; + import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; @@ -27,12 +29,26 @@ import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultBeanNameGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.test.tools.TestCompiler; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; +import org.springframework.data.mongodb.core.convert.MongoConverter; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions; +import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; +import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; +import org.springframework.data.repository.config.RepositoryConfigurationSource; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; @@ -53,23 +69,54 @@ import org.springframework.util.ReflectionUtils; public class AotFragmentTestConfigurationSupport implements BeanFactoryPostProcessor { private final Class repositoryInterface; + private final RepositoryConfigurationSource configSource; private final boolean registerFragmentFacade; public AotFragmentTestConfigurationSupport(Class repositoryInterface) { - this(repositoryInterface, true); + this(repositoryInterface, SampleConfiguration.class, true); + } + + public AotFragmentTestConfigurationSupport(Class repositoryInterface, Class configClass) { + this(repositoryInterface, configClass, true); } public AotFragmentTestConfigurationSupport(Class repositoryInterface, boolean registerFragmentFacade) { + this(repositoryInterface, SampleConfiguration.class, registerFragmentFacade); + } + + public AotFragmentTestConfigurationSupport(Class repositoryInterface, Class configClass, + boolean registerFragmentFacade) { this.repositoryInterface = repositoryInterface; this.registerFragmentFacade = registerFragmentFacade; + this.configSource = new AnnotationRepositoryConfigurationSource(AnnotationMetadata.introspect(configClass), + EnableMongoRepositories.class, new DefaultResourceLoader(), new StandardEnvironment(), + Mockito.mock(BeanDefinitionRegistry.class), DefaultBeanNameGenerator.INSTANCE); + } + + @Bean + MongoCustomConversions customConversions() { + return MongoCustomConversions.create(adapter -> adapter.useSpringDataJavaTimeCodecs()); + } + + @Bean + MongoMappingContext mongoMappingContext(MongoCustomConversions conversions) { + MongoMappingContext context = new MongoMappingContext(); + context.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); + return context; + } + + @Bean + MongoConverter mongoConverter(MongoMappingContext context, MongoCustomConversions conversions) { + MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, context); + converter.setCustomConversions(conversions); + return converter; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - TestMongoAotRepositoryContext repositoryContext = new TestMongoAotRepositoryContext(beanFactory, - repositoryInterface, null); + TestMongoAotRepositoryContext repositoryContext = getRepositoryContext(beanFactory); TestGenerationContext generationContext = new TestGenerationContext(repositoryInterface); new MongoRepositoryContributor(repositoryContext).contribute(generationContext); @@ -101,6 +148,10 @@ public class AotFragmentTestConfigurationSupport implements BeanFactoryPostProce beanFactory.registerSingleton("generationContext", generationContext); } + public TestMongoAotRepositoryContext getRepositoryContext(ConfigurableListableBeanFactory beanFactory) { + return new TestMongoAotRepositoryContext(beanFactory, repositoryInterface, configSource); + } + private Object getFragmentFacadeProxy(Object fragment) { return Proxy.newProxyInstance(repositoryInterface.getClassLoader(), new Class[] { repositoryInterface }, @@ -157,4 +208,10 @@ public class AotFragmentTestConfigurationSupport implements BeanFactoryPostProce } } + @EnableMongoRepositories(considerNestedRepositories = true, includeFilters = { + @ComponentScan.Filter(classes = SampleConfiguration.class, type = FilterType.ASSIGNABLE_TYPE) }) + public static class SampleConfiguration { + + } + } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorUnitTests.java index 495dbe4f9..09db4c4c0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorUnitTests.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.repository.aot; -import static org.mockito.Mockito.mock; -import static org.springframework.data.mongodb.test.util.Assertions.assertThat; +import static org.mockito.Mockito.*; +import static org.springframework.data.mongodb.test.util.Assertions.*; import example.aot.User; import example.aot.UserRepository; @@ -25,15 +25,20 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; + import org.springframework.aot.generate.GeneratedFiles; import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamSource; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.repository.Meta; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.data.repository.CrudRepository; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @@ -47,15 +52,19 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; class MongoRepositoryContributorUnitTests { @Configuration + @EnableMongoRepositories(considerNestedRepositories = true, mongoTemplateRef = "mongoOperations", + includeFilters = { @ComponentScan.Filter(classes = MetaUserRepository.class, type = FilterType.ASSIGNABLE_TYPE) }) static class MongoRepositoryContributorConfiguration extends AotFragmentTestConfigurationSupport { public MongoRepositoryContributorConfiguration() { - super(MetaUserRepository.class); + super(MetaUserRepository.class, MongoRepositoryContributorConfiguration.class); } @Bean - MongoOperations mongoOperations() { - return mock(MongoOperations.class); + MongoOperations mongoOperations(MongoConverter mongoConverter) { + MongoOperations operations = mock(MongoOperations.class); + when(operations.getConverter()).thenReturn(mongoConverter); + return operations; } } @@ -66,7 +75,8 @@ class MongoRepositoryContributorUnitTests { void shouldConsiderMetaAnnotation() throws IOException { InputStreamSource aotFragment = generationContext.getGeneratedFiles().getGeneratedFile(GeneratedFiles.Kind.SOURCE, - MetaUserRepository.class.getPackageName().replace('.', '/') + "/MetaUserRepositoryImpl__AotRepository.java"); + AotFragmentTestConfigurationSupport.getAotImplFragmentName(MetaUserRepository.class).replace('.', '/') + + ".java"); String content = new InputStreamResource(aotFragment).getContentAsString(StandardCharsets.UTF_8); @@ -76,7 +86,7 @@ class MongoRepositoryContributorUnitTests { assertThat(content).contains("filterQuery.diskUse(DiskUse.DENY)"); } - interface MetaUserRepository extends CrudRepository { + public interface MetaUserRepository extends CrudRepository { @Meta User findAllByLastname(String lastname); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryMetadataTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryMetadataTests.java index 29b6c0e86..f5feadf96 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryMetadataTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryMetadataTests.java @@ -34,6 +34,7 @@ import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; /** @@ -41,7 +42,6 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; * * @author Mark Paluch */ - @SpringJUnitConfig(classes = MongoRepositoryMetadataTests.MongoRepositoryContributorConfiguration.class) class MongoRepositoryMetadataTests { @@ -53,8 +53,10 @@ class MongoRepositoryMetadataTests { } @Bean - MongoOperations mongoOperations() { - return mock(MongoOperations.class); + MongoOperations mongoOperations(MongoConverter mongoConverter) { + MongoOperations operations = mock(MongoOperations.class); + when(operations.getConverter()).thenReturn(mongoConverter); + return operations; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/TestMongoAotRepositoryContext.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/TestMongoAotRepositoryContext.java index e588a0981..8ae14dd13 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/TestMongoAotRepositoryContext.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/TestMongoAotRepositoryContext.java @@ -18,8 +18,6 @@ package org.springframework.data.mongodb.repository.aot; import java.lang.annotation.Annotation; import java.util.Set; -import org.jspecify.annotations.Nullable; - import org.springframework.beans.factory.BeanFactory; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.env.StandardEnvironment; @@ -41,21 +39,20 @@ import org.springframework.data.repository.core.support.RepositoryComposition; public class TestMongoAotRepositoryContext extends AotRepositoryContextSupport { private final AotRepositoryInformation repositoryInformation; - private final Class repositoryInterface; + private final RepositoryConfigurationSource configurationSource; public TestMongoAotRepositoryContext(BeanFactory beanFactory, Class repositoryInterface, - @Nullable RepositoryComposition composition) { - super(AotContext.from(beanFactory, new StandardEnvironment())); + RepositoryConfigurationSource configurationSource) { - this.repositoryInterface = repositoryInterface; + super(AotContext.from(beanFactory, new StandardEnvironment())); RepositoryMetadata metadata = AnnotationRepositoryMetadata.getMetadata(repositoryInterface); - RepositoryComposition.RepositoryFragments fragments = MongoRepositoryFragmentsContributor.DEFAULT .describe(metadata); this.repositoryInformation = new AotRepositoryInformation(metadata, SimpleMongoRepository.class, fragments.stream().toList()); + this.configurationSource = configurationSource; } @Override @@ -65,12 +62,7 @@ public class TestMongoAotRepositoryContext extends AotRepositoryContextSupport { @Override public RepositoryConfigurationSource getConfigurationSource() { - return null; - } - - @Override - public Set getBasePackages() { - return Set.of(repositoryInterface.getPackageName()); + return configurationSource; } @Override