From fb3dc9ac3f33c9ef7569e3203c3de5f5c790e70b Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 4 Dec 2025 09:12:54 +0100 Subject: [PATCH 1/2] Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 557e1ad29d..b9e072a2e8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 5.1.0-SNAPSHOT + 5.1.x-GH-5107-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index cab02fe276..559c3b0074 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -15,7 +15,7 @@ org.springframework.data spring-data-mongodb-parent - 5.1.0-SNAPSHOT + 5.1.x-GH-5107-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index ec1af04ae0..69251865f9 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 5.1.0-SNAPSHOT + 5.1.x-GH-5107-SNAPSHOT ../pom.xml From e78a3ab996fd7b0e0ebd25a6f1b818037d4df368 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 4 Dec 2025 10:59:23 +0100 Subject: [PATCH 2/2] Use explicitly configured MongoOperations for AOT fragment bootstrap. This commit makes sure to use a configured MongoOperations reference to bootstrap AOT generated repository implementations. Previously mongoTemplateRef set via EnableMongoRepositories had not been taken into account. --- .../aot/MongoRepositoryContributor.java | 27 ++++-- ...positoryContributorConfigurationTests.java | 88 +++++++++++++++++++ 2 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorConfigurationTests.java 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 769afbdb0d..4222f98276 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 @@ -25,7 +25,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jspecify.annotations.Nullable; - +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.data.mapping.model.SimpleTypeHolder; @@ -72,6 +72,7 @@ public class MongoRepositoryContributor extends RepositoryContributor { private final SimpleTypeHolder simpleTypeHolder; private final MongoMappingContext mappingContext; private final NamedQueries namedQueries; + private final @Nullable String mongoOperationsRef; public MongoRepositoryContributor(AotRepositoryContext repositoryContext) { @@ -81,7 +82,9 @@ public MongoRepositoryContributor(AotRepositoryContext repositoryContext) { if (classLoader == null) { classLoader = getClass().getClassLoader(); } - namedQueries = getNamedQueries(repositoryContext.getConfigurationSource(), classLoader); + + this.namedQueries = getNamedQueries(repositoryContext.getConfigurationSource(), classLoader); + this.mongoOperationsRef = getMongoTemplateRef(repositoryContext.getConfigurationSource()); // avoid Java Time (JSR-310) Type introspection MongoCustomConversions mongoCustomConversions = MongoCustomConversions @@ -97,6 +100,14 @@ public MongoRepositoryContributor(AotRepositoryContext repositoryContext) { 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) { @@ -132,9 +143,15 @@ protected void customizeClass(AotRepositoryClassBuilder classBuilder) { @Override protected void customizeConstructor(AotRepositoryConstructorBuilder constructorBuilder) { - constructorBuilder.addParameter("operations", MongoOperations.class); - constructorBuilder.addParameter("context", RepositoryFactoryBeanSupport.FragmentCreationContext.class, - false); + constructorBuilder.addParameter("operations", MongoOperations.class, customizer -> { + + customizer.bindToField() + .origin(StringUtils.hasText(this.mongoOperationsRef) + ? new RuntimeBeanReference(this.mongoOperationsRef, MongoOperations.class) + : new RuntimeBeanReference(MongoOperations.class)); + }); + + constructorBuilder.addParameter("context", RepositoryFactoryBeanSupport.FragmentCreationContext.class, false); constructorBuilder.customize((builder) -> { builder.addStatement("super(operations, context)"); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorConfigurationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorConfigurationTests.java new file mode 100644 index 0000000000..17a9c99ebf --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorConfigurationTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2025-present 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.mongodb.repository.aot; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; +import org.springframework.aot.generate.GeneratedFiles.Kind; +import org.springframework.aot.test.generate.TestGenerationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.aot.ApplicationContextAotGenerator; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.InputStreamSource; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +/** + * @author Christoph Strobl + */ +class MongoRepositoryContributorConfigurationTests { + + @Configuration + @EnableMongoRepositories(basePackages = "example.aot") + static class ConfigurationWithoutAnythingSpecial { + + } + + @Configuration + @EnableMongoRepositories(mongoTemplateRef = "template-2", basePackages = "example.aot") + static class ConfigurationWithTemplateRef { + + } + + @Test // GH-5107 + void usesPrimaryMongoOperationsBeanReferenceByDefault() throws IOException { + + TestGenerationContext testContext = generate(ConfigurationWithoutAnythingSpecial.class); + InputStreamSource file = testContext.getGeneratedFiles().getGeneratedFile(Kind.SOURCE, + "example/aot/UserRepository__BeanDefinitions.java"); + + InputStreamResource isr = new InputStreamResource(file); + String sourceCode = isr.getContentAsString(StandardCharsets.UTF_8); + + assertThat(sourceCode).contains("operations = beanFactory.getBean(MongoOperations.class)"); + } + + @Test // GH-5107 + void shouldConsiderMongoTemplateReferenceIfPresent() throws IOException { + + TestGenerationContext testContext = generate(ConfigurationWithTemplateRef.class); + InputStreamSource file = testContext.getGeneratedFiles().getGeneratedFile(Kind.SOURCE, + "example/aot/UserRepository__BeanDefinitions.java"); + + InputStreamResource isr = new InputStreamResource(file); + String sourceCode = isr.getContentAsString(StandardCharsets.UTF_8); + + assertThat(sourceCode).contains("operations = beanFactory.getBean(\"template-2\", MongoOperations.class)"); + } + + private static TestGenerationContext generate(Class... configurationClasses) { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(configurationClasses); + + ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); + + TestGenerationContext generationContext = new TestGenerationContext(); + generator.processAheadOfTime(context, generationContext); + generationContext.writeGeneratedContent(); + return generationContext; + } +}