From ec78e1b8a1590dd7559cf763cc7bddc0bc93f9ed 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 eb46371c1..ddc85065f 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 static org.springframework.data.repository.util.ClassUtils.*; 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 abstract class RepositoryInformationSupport implements RepositoryInformat 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 abstract class RepositoryInformationSupport implements RepositoryInformat @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 @@ public abstract class RepositoryInformationSupport implements RepositoryInformat && !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(); + } + } }