Skip to content

Commit 658c04d

Browse files
mp911dechristophstrobl
authored andcommitted
Rename RepositoryMethodMetadata and move it to an interceptor.
Rename RepositoryMethodMetadata to RepositoryMethodContext. Add flags to enable method metadata exposure. See: #3090 Original Pull Request: #3093
1 parent 647d9fd commit 658c04d

13 files changed

+296
-255
lines changed

Diff for: src/main/antora/modules/ROOT/pages/repositories/custom-implementations.adoc

+6-5
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ package com.acme.search;
269269
270270
import org.springframework.beans.factory.annotation.Autowired;
271271
import org.springframework.data.domain.Limit;
272-
import org.springframework.data.repository.core.support.RepositoryMethodMetadata;
272+
import org.springframework.data.repository.core.support.RepositoryMethodContext;
273273
274274
class DefaultSearchExtension<T> implements SearchExtension<T> {
275275
@@ -280,12 +280,12 @@ class DefaultSearchExtension<T> implements SearchExtension<T> {
280280
}
281281
282282
public List<T> search(String text, Limit limit) {
283-
return search(RepositoryMethodMetadata.get(), text, limit);
283+
return search(RepositoryMethodContext.currentMethod(), text, limit);
284284
}
285285
286-
List<T> search(RepositoryMethodMetadata metadata, String text, Limit limit) {
286+
List<T> search(RepositoryMethodContext metadata, String text, Limit limit) {
287287
288-
Class<T> domainType = metadata.repository().getDomainType();
288+
Class<T> domainType = metadata.getRepository().getDomainType();
289289
290290
String indexName = domainType.getSimpleName().toLowerCase();
291291
List<String> jsonResult = service.search(indexName, text, 0, limit.max());
@@ -312,7 +312,8 @@ com.acme.search.SearchExtension=com.acme.search.DefaultSearchExtension
312312
----
313313
====
314314

315-
To make use of the extension simply add the interface to the repository as shown below. The infrastructure will take care placing the required `RepositoryMethodMetadata` so all that
315+
To make use of the extension simply add the interface to the repository as shown below.
316+
The infrastructure will take care placing the required `RepositoryMethodContext` so all that
316317

317318
====
318319
[source,java]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.repository.core.support;
17+
18+
import java.lang.reflect.Method;
19+
20+
import org.springframework.core.NamedThreadLocal;
21+
import org.springframework.data.repository.core.RepositoryMetadata;
22+
import org.springframework.lang.Nullable;
23+
24+
/**
25+
* Class containing value objects providing information about the current repository method invocation.
26+
*
27+
* @author Christoph Strobl
28+
* @author Mark Paluch
29+
*/
30+
class DefaultRepositoryMethodContext implements RepositoryMethodContext {
31+
32+
/**
33+
* ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
34+
* "exposeMetadata" property on the controlling repository factory configuration has been set to "true".
35+
*/
36+
private static final ThreadLocal<RepositoryMethodContext> currentMethod = new NamedThreadLocal<>(
37+
"Current Repository Method");
38+
39+
private final RepositoryMetadata repositoryMetadata;
40+
private final Method method;
41+
42+
public DefaultRepositoryMethodContext(RepositoryMetadata repositoryMetadata, Method method) {
43+
this.repositoryMetadata = repositoryMetadata;
44+
this.method = method;
45+
}
46+
47+
@Nullable
48+
public static RepositoryMethodContext getMetadata() {
49+
return currentMethod.get();
50+
}
51+
52+
@Nullable
53+
public static RepositoryMethodContext setMetadata(@Nullable RepositoryMethodContext metadata) {
54+
55+
RepositoryMethodContext old = currentMethod.get();
56+
if (metadata != null) {
57+
currentMethod.set(metadata);
58+
} else {
59+
currentMethod.remove();
60+
}
61+
62+
return old;
63+
}
64+
65+
@Override
66+
public RepositoryMetadata getRepository() {
67+
return repositoryMetadata;
68+
}
69+
70+
@Override
71+
public Method getMethod() {
72+
return method;
73+
}
74+
75+
}

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

-79
This file was deleted.

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.springframework.data.repository.core.RepositoryInformation;
3434
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.DefaultRepositoryInvocationMulticaster;
3535
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
36-
import org.springframework.data.repository.core.support.RepositoryMethodMetadata.MethodMetadata;
3736
import org.springframework.data.repository.query.QueryCreationException;
3837
import org.springframework.data.repository.query.QueryLookupStrategy;
3938
import org.springframework.data.repository.query.QueryMethod;
@@ -164,9 +163,7 @@ private Object doInvoke(MethodInvocation invocation) throws Throwable {
164163
RepositoryMethodInvoker invocationMetadata = invocationMetadataCache.get(method);
165164

166165
if (invocationMetadata == null) {
167-
168-
DefaultRepositoryMethodMetadata repositoryMethodMetadata = DefaultRepositoryMethodMetadata.repositoryMethodMetadata(repositoryInformation, method);
169-
invocationMetadata = RepositoryMethodInvoker.forRepositoryQuery(repositoryMethodMetadata, queries.get(method));
166+
invocationMetadata = RepositoryMethodInvoker.forRepositoryQuery(method, queries.get(method));
170167
invocationMetadataCache.put(method, invocationMetadata);
171168
}
172169

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

+6-13
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.data.repository.core.RepositoryMetadata;
3333
import org.springframework.data.repository.core.support.MethodLookup.InvokedMethod;
3434
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
35-
import org.springframework.data.repository.core.support.RepositoryMethodMetadata.MethodMetadata;
3635
import org.springframework.data.repository.util.ReactiveWrapperConverters;
3736
import org.springframework.data.util.ReactiveWrappers;
3837
import org.springframework.data.util.Streamable;
@@ -282,7 +281,7 @@ Object invoke(RepositoryInvocationMulticaster listener, Method method, Object[]
282281

283282
ReflectionUtils.makeAccessible(methodToCall);
284283

285-
return fragments.invoke(metadata, listener,
284+
return fragments.invoke(metadata != null ? metadata.getRepositoryInterface() : method.getDeclaringClass(), listener,
286285
method, methodToCall, argumentConverter.apply(methodToCall, args));
287286
}
288287

@@ -370,6 +369,7 @@ public static class RepositoryFragments implements Streamable<RepositoryFragment
370369
private final List<RepositoryFragment<?>> fragments;
371370

372371
private RepositoryFragments(List<RepositoryFragment<?>> fragments) {
372+
373373
this.fragments = fragments;
374374
}
375375

@@ -382,10 +382,6 @@ public static RepositoryFragments empty() {
382382
return EMPTY;
383383
}
384384

385-
public static RepositoryFragments empty(RepositoryMetadata metadata) {
386-
return EMPTY;
387-
}
388-
389385
/**
390386
* Create {@link RepositoryFragments} from just implementation objects.
391387
*
@@ -488,7 +484,7 @@ public Object invoke(Method invokedMethod, Method methodToCall, Object[] args) t
488484
/**
489485
* Invoke {@link Method} by resolving the fragment that implements a suitable method.
490486
*
491-
* @param metadata
487+
* @param repositoryInterface
492488
* @param listener
493489
* @param invokedMethod invoked method as per invocation on the interface.
494490
* @param methodToCall backend method that is backing the call.
@@ -497,7 +493,7 @@ public Object invoke(Method invokedMethod, Method methodToCall, Object[] args) t
497493
* @throws Throwable
498494
*/
499495
@Nullable
500-
Object invoke(@Nullable RepositoryMetadata metadata, RepositoryInvocationMulticaster listener, Method invokedMethod,
496+
Object invoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster listener, Method invokedMethod,
501497
Method methodToCall, Object[] args) throws Throwable {
502498

503499
RepositoryFragment<?> fragment = fragmentCache.computeIfAbsent(methodToCall, this::findImplementationFragment);
@@ -511,15 +507,12 @@ Object invoke(@Nullable RepositoryMetadata metadata, RepositoryInvocationMultica
511507

512508
if (repositoryMethodInvoker == null) {
513509

514-
DefaultRepositoryMethodMetadata repositoryMethodMetadata = DefaultRepositoryMethodMetadata.repositoryMethodMetadata(metadata, invokedMethod, methodToCall);
515-
repositoryMethodInvoker = RepositoryMethodInvoker.forFragmentMethod(repositoryMethodMetadata, optional.get(),
510+
repositoryMethodInvoker = RepositoryMethodInvoker.forFragmentMethod(invokedMethod, optional.get(),
516511
methodToCall);
517-
518512
invocationMetadataCache.put(invokedMethod, repositoryMethodInvoker);
519513
}
520514

521-
Class<?> target = (metadata != null && metadata.getRepositoryInterface() != null) ? metadata.getRepositoryInterface() : invokedMethod.getDeclaringClass();
522-
return repositoryMethodInvoker.invoke(target, listener, args);
515+
return repositoryMethodInvoker.invoke(repositoryInterface, listener, args);
523516
}
524517

525518
private RepositoryFragment<?> findImplementationFragment(Method key) {

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

+14
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
6969
private final Class<? extends T> repositoryInterface;
7070

7171
private RepositoryFactorySupport factory;
72+
private boolean exposeMetadata;
7273
private Key queryLookupStrategyKey;
7374
private Optional<Class<?>> repositoryBaseClass = Optional.empty();
7475
private Optional<Object> customImplementation = Optional.empty();
@@ -107,6 +108,18 @@ public void setRepositoryBaseClass(Class<?> repositoryBaseClass) {
107108
this.repositoryBaseClass = Optional.ofNullable(repositoryBaseClass);
108109
}
109110

111+
/**
112+
* Set whether the repository method metadata should be exposed by the repository factory as a ThreadLocal for
113+
* retrieval via the {@code RepositoryMethodContext} class. This is useful if an advised object needs to obtain
114+
* repository information.
115+
* <p>
116+
* Default is "false", in order to avoid unnecessary extra interception. This means that no guarantees are provided
117+
* that {@code RepositoryMethodContext} access will work consistently within any method of the advised object.
118+
*/
119+
public void setExposeMetadata(boolean exposeMetadata) {
120+
this.exposeMetadata = exposeMetadata;
121+
}
122+
110123
/**
111124
* Set the {@link QueryLookupStrategy.Key} to be used.
112125
*
@@ -258,6 +271,7 @@ public boolean isSingleton() {
258271
public void afterPropertiesSet() {
259272

260273
this.factory = createRepositoryFactory();
274+
this.factory.setExposeMetadata(exposeMetadata);
261275
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
262276
this.factory.setNamedQueries(namedQueries);
263277
this.factory.setEvaluationContextProvider(

0 commit comments

Comments
 (0)