From 1e15775db09ad42689c9f3dfe33440653f565490 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 17 Jul 2024 11:44:00 +0200 Subject: [PATCH 1/4] Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-benchmarks/pom.xml | 2 +- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index f518c186f5..ff741d5be9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 4.4.0-SNAPSHOT + 4.4.x-GH-4744-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index a3dc49f892..973dbb4522 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 4.4.0-SNAPSHOT + 4.4.x-GH-4744-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index e33930bfd2..3a275dc0ab 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 - 4.4.0-SNAPSHOT + 4.4.x-GH-4744-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index fafe9c8793..939d481f5e 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 4.4.0-SNAPSHOT + 4.4.x-GH-4744-SNAPSHOT ../pom.xml From 78677ed15cacfe251b96ff0d9b9e769d97cb4d9b Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 17 Jul 2024 13:31:48 +0200 Subject: [PATCH 2/4] hacking --- .../aot/RepositoryRuntimeHints.java | 16 +++++++ .../mongodb/repository/query/QueryUtils.java | 44 +++++++++++++------ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java index 203e5e9810..43eb0ec763 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java @@ -19,11 +19,15 @@ import java.util.List; +import org.bson.Document; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.TypeReference; import org.springframework.data.mongodb.aot.MongoAotPredicates; +import org.springframework.data.mongodb.core.query.BasicQuery; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.repository.query.QueryUtils; import org.springframework.data.mongodb.repository.support.CrudMethodMetadata; import org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor; import org.springframework.data.mongodb.repository.support.ReactiveQuerydslMongoPredicateExecutor; @@ -45,6 +49,18 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)); + Query query = QueryUtils.decorateSort(new BasicQuery("{}", null), new Document("foo", "bar")); + + hints.reflection() + .registerType(Query.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); + hints.reflection() + .registerType(BasicQuery.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); + hints.reflection() + .registerType(query.getClass(), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); + if (isAopPresent(classLoader)) { // required for pushing ReadPreference,... into the default repository implementation diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java index 1aab34055a..f9a2055754 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java @@ -19,12 +19,18 @@ import java.util.List; import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; import org.bson.Document; +import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.target.SingletonTargetSource; +import org.springframework.data.mongodb.core.ReadConcernAware; +import org.springframework.data.mongodb.core.ReadPreferenceAware; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.expression.ExpressionParser; +import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; @@ -37,7 +43,7 @@ * @since 2.1 * @currentRead Assassin's Apprentice - Robin Hobb */ -class QueryUtils { +public class QueryUtils { /** * Decorate {@link Query} and add a default sort expression to the given {@link Query}. Attributes of the given @@ -47,25 +53,15 @@ class QueryUtils { * @param defaultSort the default sort expression to apply to the query. * @return the query having the given {@code sort} applied. */ - static Query decorateSort(Query query, Document defaultSort) { + public static Query decorateSort(Query query, Document defaultSort) { if (defaultSort.isEmpty()) { return query; } ProxyFactory factory = new ProxyFactory(query); - factory.addAdvice((MethodInterceptor) invocation -> { - - if (!invocation.getMethod().getName().equals("getSortObject")) { - return invocation.proceed(); - } - - Document combinedSort = new Document(defaultSort); - combinedSort.putAll((Document) invocation.proceed()); - return combinedSort; - }); + factory.addAdvice(new MyMethodInterceptor(defaultSort)); factory.setInterfaces(new Class[0]); - return (Query) factory.getProxy(query.getClass().getClassLoader()); } @@ -124,4 +120,26 @@ static int indexOfAssignableParameter(Class type, List> parameters) } return -1; } + + static class MyMethodInterceptor implements MethodInterceptor { + + private final Document defaultSort; + + public MyMethodInterceptor(Document defaultSort) { + this.defaultSort = defaultSort; + } + + @Nullable + @Override + public Object invoke(@NonNull MethodInvocation invocation) throws Throwable { + + if (!invocation.getMethod().getName().equals("getSortObject")) { + return invocation.proceed(); + } + + Document combinedSort = new Document(defaultSort); + combinedSort.putAll((Document) invocation.proceed()); + return combinedSort; + } + } } From a32d471a74f701563e66ff5debc2f5d1249e9518 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 17 Jul 2024 14:46:56 +0200 Subject: [PATCH 3/4] Generate proxy and register reflection for default sorting during AOT. This commit makes sure to generate required cglib proxies during AOT phase so they are ready to use within a native image. Prior to this change default sorting raised an error as class generation is not allowed in a GraalVM native image. --- .../aot/LazyLoadingProxyAotProcessor.java | 4 +-- .../mongodb/aot/MongoAotReflectionHelper.java | 31 ++++++++++++++++ .../aot/RepositoryRuntimeHints.java | 24 ++++++------- .../mongodb/repository/query/QueryUtils.java | 36 ++++++++++++++----- 4 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotReflectionHelper.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java index 09080c32d5..530ffce510 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java @@ -24,7 +24,6 @@ import java.util.Set; import org.springframework.aot.generate.GenerationContext; -import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.TypeReference; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.MergedAnnotations; @@ -77,8 +76,7 @@ public void registerLazyLoadingProxyIfNeeded(Class type, GenerationContext ge LazyLoadingInterceptor::none); // see: spring-projects/spring-framework/issues/29309 - generationContext.getRuntimeHints().reflection().registerType(proxyClass, - MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); + generationContext.getRuntimeHints().reflection().registerType(proxyClass, MongoAotReflectionHelper::cglibProxyReflectionMemberAccess); } }); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotReflectionHelper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotReflectionHelper.java new file mode 100644 index 0000000000..e3d3a30336 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotReflectionHelper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2024 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.aot; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.TypeHint.Builder; + +/** + * @author Christoph Strobl + */ +public final class MongoAotReflectionHelper { + + public static void cglibProxyReflectionMemberAccess(Builder builder) { + + builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.DECLARED_FIELDS); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java index 43eb0ec763..8ed6ea66c4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java @@ -19,12 +19,12 @@ import java.util.List; -import org.bson.Document; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.TypeReference; import org.springframework.data.mongodb.aot.MongoAotPredicates; +import org.springframework.data.mongodb.aot.MongoAotReflectionHelper; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.query.QueryUtils; @@ -49,17 +49,7 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)); - Query query = QueryUtils.decorateSort(new BasicQuery("{}", null), new Document("foo", "bar")); - - hints.reflection() - .registerType(Query.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, - MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); - hints.reflection() - .registerType(BasicQuery.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, - MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); - hints.reflection() - .registerType(query.getClass(), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, - MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS); + registerHintsForDefaultSorting(hints, classLoader); if (isAopPresent(classLoader)) { @@ -108,4 +98,14 @@ private static void registerQuerydslHints(RuntimeHints hints, @Nullable ClassLoa private static boolean isAopPresent(@Nullable ClassLoader classLoader) { return ClassUtils.isPresent("org.springframework.aop.Pointcut", classLoader); } + + private static void registerHintsForDefaultSorting(RuntimeHints hints, @Nullable ClassLoader classLoader) { + + List types = List.of(TypeReference.of(Query.class), // + TypeReference.of(QueryUtils.queryProxyType(Query.class, classLoader)), // + TypeReference.of(BasicQuery.class), // + TypeReference.of(QueryUtils.queryProxyType(BasicQuery.class, classLoader))); + + hints.reflection().registerTypes(types, MongoAotReflectionHelper::cglibProxyReflectionMemberAccess); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java index f9a2055754..c7cb84d091 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java @@ -20,12 +20,10 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.bson.Document; -import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.aop.target.SingletonTargetSource; -import org.springframework.data.mongodb.core.ReadConcernAware; -import org.springframework.data.mongodb.core.ReadPreferenceAware; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; @@ -45,6 +43,8 @@ */ public class QueryUtils { + protected static final Log LOGGER = LogFactory.getLog(QueryUtils.class); + /** * Decorate {@link Query} and add a default sort expression to the given {@link Query}. Attributes of the given * {@code sort} may be overwritten by the sort explicitly defined by the {@link Query} itself. @@ -59,12 +59,21 @@ public static Query decorateSort(Query query, Document defaultSort) { return query; } - ProxyFactory factory = new ProxyFactory(query); - factory.addAdvice(new MyMethodInterceptor(defaultSort)); - factory.setInterfaces(new Class[0]); + ProxyFactory factory = prepareQueryProxy(query.getClass(), defaultSort); + factory.setTarget(query); return (Query) factory.getProxy(query.getClass().getClassLoader()); } + /** + * Decorate {@link Query} and add a default sort expression to the given {@link Query}. Attributes of the given + * {@code sort} may be overwritten by the sort explicitly defined by the {@link Query} itself. + * + * @param classLoader the {@link ClassLoader} to use for generating the proxy type with. + */ + public static Class queryProxyType(Class baseType, ClassLoader classLoader) { + return prepareQueryProxy(baseType, new Document()).getProxyClass(classLoader); + } + /** * Apply a collation extracted from the given {@literal collationExpression} to the given {@link Query}. Potentially * replace parameter placeholders with values from the {@link ConvertingParameterAccessor accessor}. @@ -121,11 +130,20 @@ static int indexOfAssignableParameter(Class type, List> parameters) return -1; } - static class MyMethodInterceptor implements MethodInterceptor { + private static ProxyFactory prepareQueryProxy(Class query, Document defaultSort) { + + ProxyFactory factory = new ProxyFactory(); + factory.setTargetClass(query); + factory.addAdvice(new DefaultSortingInterceptor(defaultSort)); + factory.setInterfaces(new Class[0]); + return factory; + } + + static class DefaultSortingInterceptor implements MethodInterceptor { private final Document defaultSort; - public MyMethodInterceptor(Document defaultSort) { + public DefaultSortingInterceptor(Document defaultSort) { this.defaultSort = defaultSort; } From dce2bf840c5f713e09196610c542eb8689a74eca Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 23 Jul 2024 08:56:21 +0200 Subject: [PATCH 4/4] Add copy constructor to BasicQuery. Removes the need for proxying. --- .../data/mongodb/core/query/BasicQuery.java | 14 ++++ .../data/mongodb/core/query/Query.java | 14 ++++ .../aot/RepositoryRuntimeHints.java | 15 ----- .../repository/query/AbstractMongoQuery.java | 3 +- .../mongodb/repository/query/QueryUtils.java | 64 ++++--------------- 5 files changed, 43 insertions(+), 67 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java index b381a21593..79ceeda95e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java @@ -85,6 +85,20 @@ public BasicQuery(Document queryObject, Document fieldsObject) { this.sortObject = new Document(); } + /** + * Create a BasicQuery given a {@link Query}. The resulting query is a copy of {@link Query}. + * + * @param query + */ + public BasicQuery(Query query) { + + super(query); + this.queryObject = query.getQueryObject(); + this.setFieldsObject(query.getFieldsObject()); + this.setSortObject(query.getSortObject()); + this.setMeta(query.getMeta()); + } + @Override public Query addCriteria(CriteriaDefinition criteria) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java index 2b307f15c7..d79b22aef7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java @@ -79,6 +79,20 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { private Optional collation = Optional.empty(); + Query(Query query) { + this.restrictedTypes = query.restrictedTypes; + this.fieldSpec = query.fieldSpec; + this.sort = query.sort; + this.limit = query.limit; + this.skip = query.skip; + this.keysetScrollPosition = query.keysetScrollPosition; + this.readConcern = query.readConcern; + this.readPreference = query.readPreference; + this.hint = query.hint; + this.meta = query.meta; + this.collation = query.collation; + } + /** * Static factory method to create a {@link Query} using the provided {@link CriteriaDefinition}. * diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java index 8ed6ea66c4..46e6c9044c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java @@ -24,10 +24,6 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.TypeReference; import org.springframework.data.mongodb.aot.MongoAotPredicates; -import org.springframework.data.mongodb.aot.MongoAotReflectionHelper; -import org.springframework.data.mongodb.core.query.BasicQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.repository.query.QueryUtils; import org.springframework.data.mongodb.repository.support.CrudMethodMetadata; import org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor; import org.springframework.data.mongodb.repository.support.ReactiveQuerydslMongoPredicateExecutor; @@ -49,8 +45,6 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)); - registerHintsForDefaultSorting(hints, classLoader); - if (isAopPresent(classLoader)) { // required for pushing ReadPreference,... into the default repository implementation @@ -99,13 +93,4 @@ private static boolean isAopPresent(@Nullable ClassLoader classLoader) { return ClassUtils.isPresent("org.springframework.aop.Pointcut", classLoader); } - private static void registerHintsForDefaultSorting(RuntimeHints hints, @Nullable ClassLoader classLoader) { - - List types = List.of(TypeReference.of(Query.class), // - TypeReference.of(QueryUtils.queryProxyType(Query.class, classLoader)), // - TypeReference.of(BasicQuery.class), // - TypeReference.of(QueryUtils.queryProxyType(BasicQuery.class, classLoader))); - - hints.reflection().registerTypes(types, MongoAotReflectionHelper::cglibProxyReflectionMemberAccess); - } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java index 52eef58340..61bfa0f7b3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java @@ -20,6 +20,7 @@ import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; + import org.springframework.data.mapping.model.SpELExpressionEvaluator; import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery; @@ -155,7 +156,7 @@ protected Object doExecute(MongoQueryMethod method, ResultProcessor processor, C * @since 4.2 */ private Query applyAnnotatedReadPreferenceIfPresent(Query query) { - + if (!method.hasAnnotatedReadPreference()) { return query; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java index c7cb84d091..c6ad7a634f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java @@ -18,17 +18,15 @@ import java.util.Arrays; import java.util.List; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.Document; -import org.springframework.aop.framework.ProxyFactory; + +import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.expression.ExpressionParser; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; @@ -41,7 +39,7 @@ * @since 2.1 * @currentRead Assassin's Apprentice - Robin Hobb */ -public class QueryUtils { +class QueryUtils { protected static final Log LOGGER = LogFactory.getLog(QueryUtils.class); @@ -53,25 +51,19 @@ public class QueryUtils { * @param defaultSort the default sort expression to apply to the query. * @return the query having the given {@code sort} applied. */ - public static Query decorateSort(Query query, Document defaultSort) { + static Query decorateSort(Query query, Document defaultSort) { if (defaultSort.isEmpty()) { return query; } - ProxyFactory factory = prepareQueryProxy(query.getClass(), defaultSort); - factory.setTarget(query); - return (Query) factory.getProxy(query.getClass().getClassLoader()); - } + BasicQuery defaultSortQuery = query instanceof BasicQuery bq ? bq : new BasicQuery(query); - /** - * Decorate {@link Query} and add a default sort expression to the given {@link Query}. Attributes of the given - * {@code sort} may be overwritten by the sort explicitly defined by the {@link Query} itself. - * - * @param classLoader the {@link ClassLoader} to use for generating the proxy type with. - */ - public static Class queryProxyType(Class baseType, ClassLoader classLoader) { - return prepareQueryProxy(baseType, new Document()).getProxyClass(classLoader); + Document combinedSort = new Document(defaultSort); + combinedSort.putAll(defaultSortQuery.getSortObject()); + defaultSortQuery.setSortObject(combinedSort); + + return defaultSortQuery; } /** @@ -116,13 +108,13 @@ static int indexOfAssignableParameter(Class type, Class[] parameters) { */ static int indexOfAssignableParameter(Class type, List> parameters) { - if(parameters.isEmpty()) { + if (parameters.isEmpty()) { return -1; } int i = 0; - for(Class parameterType : parameters) { - if(ClassUtils.isAssignable(type, parameterType)) { + for (Class parameterType : parameters) { + if (ClassUtils.isAssignable(type, parameterType)) { return i; } i++; @@ -130,34 +122,4 @@ static int indexOfAssignableParameter(Class type, List> parameters) return -1; } - private static ProxyFactory prepareQueryProxy(Class query, Document defaultSort) { - - ProxyFactory factory = new ProxyFactory(); - factory.setTargetClass(query); - factory.addAdvice(new DefaultSortingInterceptor(defaultSort)); - factory.setInterfaces(new Class[0]); - return factory; - } - - static class DefaultSortingInterceptor implements MethodInterceptor { - - private final Document defaultSort; - - public DefaultSortingInterceptor(Document defaultSort) { - this.defaultSort = defaultSort; - } - - @Nullable - @Override - public Object invoke(@NonNull MethodInvocation invocation) throws Throwable { - - if (!invocation.getMethod().getName().equals("getSortObject")) { - return invocation.proceed(); - } - - Document combinedSort = new Document(defaultSort); - combinedSort.putAll((Document) invocation.proceed()); - return combinedSort; - } - } }