Skip to content

Commit 8b4028d

Browse files
committed
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.
1 parent f376c51 commit 8b4028d

File tree

1 file changed

+57
-31
lines changed

1 file changed

+57
-31
lines changed

Diff for: src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java

+57-31
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919

2020
import java.lang.reflect.Method;
2121
import java.lang.reflect.Modifier;
22-
import java.util.Collections;
23-
import java.util.HashSet;
22+
import java.util.Arrays;
2423
import java.util.Set;
2524
import java.util.function.Supplier;
2625

@@ -43,6 +42,7 @@ public abstract class RepositoryInformationSupport implements RepositoryInformat
4342

4443
private final Supplier<RepositoryMetadata> metadata;
4544
private final Supplier<Class<?>> repositoryBaseClass;
45+
private final Supplier<DefaultQueryMethods> queryMethods;
4646

4747
public RepositoryInformationSupport(Supplier<RepositoryMetadata> metadata, Supplier<Class<?>> repositoryBaseClass) {
4848

@@ -51,25 +51,12 @@ public RepositoryInformationSupport(Supplier<RepositoryMetadata> metadata, Suppl
5151

5252
this.metadata = Lazy.of(metadata);
5353
this.repositoryBaseClass = Lazy.of(repositoryBaseClass);
54+
this.queryMethods = Lazy.of(this::calculateQueryMethods);
5455
}
5556

5657
@Override
5758
public Streamable<Method> getQueryMethods() {
58-
59-
Set<Method> result = new HashSet<>();
60-
61-
for (Method method : getRepositoryInterface().getMethods()) {
62-
method = ClassUtils.getMostSpecificMethod(method, getRepositoryInterface());
63-
if (isQueryMethodCandidate(method)) {
64-
result.add(method);
65-
}
66-
}
67-
68-
return Streamable.of(Collections.unmodifiableSet(result));
69-
}
70-
71-
private RepositoryMetadata getMetadata() {
72-
return metadata.get();
59+
return queryMethods.get().methods;
7360
}
7461

7562
@Override
@@ -139,21 +126,12 @@ public TypeInformation<?> getIdTypeInformation() {
139126

140127
@Override
141128
public boolean hasCustomMethod() {
129+
return queryMethods.get().hasCustomMethod;
130+
}
142131

143-
Class<?> repositoryInterface = getRepositoryInterface();
144-
145-
// No detection required if no typing interface was configured
146-
if (isGenericRepositoryInterface(repositoryInterface)) {
147-
return false;
148-
}
149-
150-
for (Method method : repositoryInterface.getMethods()) {
151-
if (isCustomMethod(method) && !isBaseClassMethod(method)) {
152-
return true;
153-
}
154-
}
155-
156-
return false;
132+
@Override
133+
public boolean hasQueryMethods() {
134+
return queryMethods.get().hasQueryMethod;
157135
}
158136

159137
/**
@@ -178,4 +156,52 @@ protected boolean isQueryMethodCandidate(Method method) {
178156
&& !Modifier.isStatic(method.getModifiers()) //
179157
&& (isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method));
180158
}
159+
160+
private RepositoryMetadata getMetadata() {
161+
return metadata.get();
162+
}
163+
164+
private final DefaultQueryMethods calculateQueryMethods() {
165+
166+
Class<?> repositoryInterface = getRepositoryInterface();
167+
168+
return new DefaultQueryMethods(Streamable.of(Arrays.stream(repositoryInterface.getMethods())
169+
.map(it -> ClassUtils.getMostSpecificMethod(it, repositoryInterface))
170+
.filter(this::isQueryMethodCandidate)
171+
.toList()), calculateHasCustomMethod(repositoryInterface));
172+
}
173+
174+
private final boolean calculateHasCustomMethod(Class<?> repositoryInterface) {
175+
176+
// No detection required if no typing interface was configured
177+
if (isGenericRepositoryInterface(repositoryInterface)) {
178+
return false;
179+
}
180+
181+
for (Method method : repositoryInterface.getMethods()) {
182+
if (isCustomMethod(method) && !isBaseClassMethod(method)) {
183+
return true;
184+
}
185+
}
186+
187+
return false;
188+
}
189+
190+
/**
191+
* Information about query methods to allow canonical computation and reuse of that information.
192+
*
193+
* @author Oliver Drotbohm
194+
*/
195+
private static class DefaultQueryMethods {
196+
197+
private final Streamable<Method> methods;
198+
private final boolean hasCustomMethod, hasQueryMethod;
199+
200+
DefaultQueryMethods(Streamable<Method> methods, boolean hasCustomMethod) {
201+
202+
this.methods = methods;
203+
this.hasCustomMethod = hasCustomMethod;
204+
this.hasQueryMethod = !methods.isEmpty();
205+
}
206+
}
181207
}

0 commit comments

Comments
 (0)