From 8b4028d6f8b1bf2c6810b0e1211758ed9caceae5 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Mon, 25 Mar 2024 12:34:12 +0100 Subject: [PATCH] Cache query method metadata to avoid repeated calculations. We now calculate information about query methods in RepositoryInformationSupport lazily and keep it around to avoid repeated calculations that involve traversals over declared method and Stream allocations. Fixes GH-3066. --- .../core/RepositoryInformationSupport.java | 88 ++++++++++++------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java b/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java index eb46371c1e..ddc85065ff 100644 --- a/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java +++ b/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java @@ -19,8 +19,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Collections; -import java.util.HashSet; +import java.util.Arrays; import java.util.Set; import java.util.function.Supplier; @@ -43,6 +42,7 @@ public abstract class RepositoryInformationSupport implements RepositoryInformat private final Supplier metadata; private final Supplier> repositoryBaseClass; + private final Supplier queryMethods; public RepositoryInformationSupport(Supplier metadata, Supplier> repositoryBaseClass) { @@ -51,25 +51,12 @@ public RepositoryInformationSupport(Supplier metadata, Suppl this.metadata = Lazy.of(metadata); this.repositoryBaseClass = Lazy.of(repositoryBaseClass); + this.queryMethods = Lazy.of(this::calculateQueryMethods); } @Override public Streamable getQueryMethods() { - - Set result = new HashSet<>(); - - for (Method method : getRepositoryInterface().getMethods()) { - method = ClassUtils.getMostSpecificMethod(method, getRepositoryInterface()); - if (isQueryMethodCandidate(method)) { - result.add(method); - } - } - - return Streamable.of(Collections.unmodifiableSet(result)); - } - - private RepositoryMetadata getMetadata() { - return metadata.get(); + return queryMethods.get().methods; } @Override @@ -139,21 +126,12 @@ public TypeInformation getIdTypeInformation() { @Override public boolean hasCustomMethod() { + return queryMethods.get().hasCustomMethod; + } - Class repositoryInterface = getRepositoryInterface(); - - // No detection required if no typing interface was configured - if (isGenericRepositoryInterface(repositoryInterface)) { - return false; - } - - for (Method method : repositoryInterface.getMethods()) { - if (isCustomMethod(method) && !isBaseClassMethod(method)) { - return true; - } - } - - return false; + @Override + public boolean hasQueryMethods() { + return queryMethods.get().hasQueryMethod; } /** @@ -178,4 +156,52 @@ protected boolean isQueryMethodCandidate(Method method) { && !Modifier.isStatic(method.getModifiers()) // && (isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method)); } + + private RepositoryMetadata getMetadata() { + return metadata.get(); + } + + private final DefaultQueryMethods calculateQueryMethods() { + + Class repositoryInterface = getRepositoryInterface(); + + return new DefaultQueryMethods(Streamable.of(Arrays.stream(repositoryInterface.getMethods()) + .map(it -> ClassUtils.getMostSpecificMethod(it, repositoryInterface)) + .filter(this::isQueryMethodCandidate) + .toList()), calculateHasCustomMethod(repositoryInterface)); + } + + private final boolean calculateHasCustomMethod(Class repositoryInterface) { + + // No detection required if no typing interface was configured + if (isGenericRepositoryInterface(repositoryInterface)) { + return false; + } + + for (Method method : repositoryInterface.getMethods()) { + if (isCustomMethod(method) && !isBaseClassMethod(method)) { + return true; + } + } + + return false; + } + + /** + * Information about query methods to allow canonical computation and reuse of that information. + * + * @author Oliver Drotbohm + */ + private static class DefaultQueryMethods { + + private final Streamable methods; + private final boolean hasCustomMethod, hasQueryMethod; + + DefaultQueryMethods(Streamable methods, boolean hasCustomMethod) { + + this.methods = methods; + this.hasCustomMethod = hasCustomMethod; + this.hasQueryMethod = !methods.isEmpty(); + } + } }