Skip to content

Commit 5aba74d

Browse files
Move responsibility for storing method context to dedicated component.
1 parent 3fb983d commit 5aba74d

File tree

5 files changed

+103
-49
lines changed

5 files changed

+103
-49
lines changed

src/main/java/org/springframework/data/repository/core/RepositoryMethodContext.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
/**
2121
* Interface containing methods and value objects to obtain information about the current repository method invocation.
2222
* <p>
23-
* The {@link #getMetadata()} method is usable if the repository factory is configured to expose the current
24-
* repository method metadata (not the default). It returns the invoked repository method. Target objects or advice can
25-
* use this to make advised calls.
23+
* The {@link #getMetadata()} method is usable if the repository factory is configured to expose the current repository
24+
* method metadata (not the default). It returns the invoked repository method. Target objects or advice can use this to
25+
* make advised calls.
2626
* <p>
2727
* Spring Data's framework does not expose method metadata by default, as there is a performance cost in doing so.
2828
* <p>
@@ -50,7 +50,7 @@ public interface RepositoryMethodContext {
5050
* The method object represents the method as being invoked on the repository interface. It doesn't match the backing
5151
* repository implementation in case the method invocation is delegated to an implementation method.
5252
*
53-
* @return the current method, will never be {@literal null}..
53+
* @return the current method, will never be {@literal null}.
5454
*/
5555
Method getMethod();
5656
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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;
17+
18+
import org.springframework.core.NamedThreadLocal;
19+
import org.springframework.lang.Nullable;
20+
21+
/**
22+
* @author Christoph Strobl
23+
* @since 3.4.0
24+
*/
25+
public class RepositoryMethodContextHolder {
26+
27+
private static ContextProvider contextSupplier;
28+
29+
static {
30+
contextSupplier = new ThreadLocalContextProvider();
31+
}
32+
33+
@Nullable
34+
public static RepositoryMethodContext setContext(@Nullable RepositoryMethodContext context) {
35+
return contextSupplier.set(context);
36+
}
37+
38+
@Nullable
39+
public static RepositoryMethodContext current() {
40+
return contextSupplier.get();
41+
}
42+
43+
public static void clearContext() {
44+
contextSupplier.clear();
45+
}
46+
47+
interface ContextProvider {
48+
49+
@Nullable
50+
RepositoryMethodContext get();
51+
52+
@Nullable
53+
RepositoryMethodContext set(@Nullable RepositoryMethodContext context);
54+
55+
void clear();
56+
}
57+
58+
static class ThreadLocalContextProvider implements ContextProvider {
59+
60+
/**
61+
* ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
62+
* "exposeMetadata" property on the controlling repository factory configuration has been set to "true".
63+
*/
64+
private static final ThreadLocal<RepositoryMethodContext> currentMethod = new NamedThreadLocal<>(
65+
"Current Repository Method");
66+
67+
@Override
68+
@Nullable
69+
public RepositoryMethodContext get() {
70+
return currentMethod.get();
71+
}
72+
73+
public void clear() {
74+
currentMethod.remove();
75+
}
76+
77+
@Override
78+
@Nullable
79+
public RepositoryMethodContext set(@Nullable RepositoryMethodContext context) {
80+
81+
RepositoryMethodContext old = currentMethod.get();
82+
83+
if (context != null) {
84+
currentMethod.set(context);
85+
} else {
86+
currentMethod.remove();
87+
}
88+
89+
return old;
90+
}
91+
}
92+
}

src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMethodContext.java

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@
1717

1818
import java.lang.reflect.Method;
1919

20-
import org.springframework.aop.framework.ProxyFactory;
21-
import org.springframework.core.NamedThreadLocal;
2220
import org.springframework.data.repository.core.RepositoryMetadata;
2321
import org.springframework.data.repository.core.RepositoryMethodContext;
24-
import org.springframework.lang.Nullable;
2522
import org.springframework.util.Assert;
2623

2724
/**
@@ -34,13 +31,6 @@
3431
*/
3532
public class DefaultRepositoryMethodContext implements RepositoryMethodContext {
3633

37-
/**
38-
* ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
39-
* "exposeMetadata" property on the controlling repository factory configuration has been set to "true".
40-
*/
41-
private static final ThreadLocal<RepositoryMethodContext> currentMethod = new NamedThreadLocal<>(
42-
"Current Repository Method");
43-
4434
private final RepositoryMetadata repositoryMetadata;
4535
private final Method method;
4636

@@ -64,36 +54,6 @@ public static RepositoryMethodContext forMethod(Method method) {
6454
method);
6555
}
6656

67-
/**
68-
* Creates a proxy {@link RepositoryMethodContext} instance suitable for dependency injection.
69-
*
70-
* @return will never be {@literal null}.
71-
*/
72-
public static RepositoryMethodContext getInjectionProxy() {
73-
74-
return ProxyFactory.getProxy(RepositoryMethodContext.class,
75-
new DynamicLookupTargetSource<>(RepositoryMethodContext.class, () -> getInstance()));
76-
}
77-
78-
@Nullable
79-
static RepositoryMethodContext getInstance() {
80-
return currentMethod.get();
81-
}
82-
83-
@Nullable
84-
static RepositoryMethodContext setMetadata(@Nullable RepositoryMethodContext metadata) {
85-
86-
RepositoryMethodContext old = currentMethod.get();
87-
88-
if (metadata != null) {
89-
currentMethod.set(metadata);
90-
} else {
91-
currentMethod.remove();
92-
}
93-
94-
return old;
95-
}
96-
9757
@Override
9858
public RepositoryMetadata getMetadata() {
9959
return repositoryMetadata;

src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.springframework.data.repository.core.RepositoryInformation;
5858
import org.springframework.data.repository.core.RepositoryMetadata;
5959
import org.springframework.data.repository.core.RepositoryMethodContext;
60+
import org.springframework.data.repository.core.RepositoryMethodContextHolder;
6061
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
6162
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.DefaultRepositoryInvocationMulticaster;
6263
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
@@ -776,13 +777,13 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
776777

777778
try {
778779

779-
oldMetadata = DefaultRepositoryMethodContext
780-
.setMetadata(new DefaultRepositoryMethodContext(repositoryMetadata, invocation.getMethod()));
780+
oldMetadata = RepositoryMethodContextHolder
781+
.setContext(new DefaultRepositoryMethodContext(repositoryMetadata, invocation.getMethod()));
781782

782783
return invocation.proceed();
783784

784785
} finally {
785-
DefaultRepositoryMethodContext.setMetadata(oldMetadata);
786+
RepositoryMethodContextHolder.setContext(oldMetadata);
786787
}
787788
}
788789
}

src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import org.springframework.data.repository.core.RepositoryInformation;
5959
import org.springframework.data.repository.core.RepositoryMetadata;
6060
import org.springframework.data.repository.core.RepositoryMethodContext;
61+
import org.springframework.data.repository.core.RepositoryMethodContextHolder;
6162
import org.springframework.data.repository.core.support.DummyRepositoryFactory.MyRepositoryQuery;
6263
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
6364
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocation;
@@ -255,7 +256,7 @@ void capturesRepositoryMetadata() {
255256
record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocation) {}
256257

257258
when(factory.queryOne.execute(any(Object[].class)))
258-
.then(invocation -> new Metadata(DefaultRepositoryMethodContext.getInstance(),
259+
.then(invocation -> new Metadata(RepositoryMethodContextHolder.current(),
259260
ExposeInvocationInterceptor.currentInvocation()));
260261

261262
factory.setExposeMetadata(true);
@@ -278,7 +279,7 @@ record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocati
278279
}
279280

280281
when(factory.queryOne.execute(any(Object[].class)))
281-
.then(invocation -> new Metadata(DefaultRepositoryMethodContext.getInstance(),
282+
.then(invocation -> new Metadata(RepositoryMethodContextHolder.current(),
282283
ExposeInvocationInterceptor.currentInvocation()));
283284

284285
var repository = factory.getRepository(ObjectRepository.class, new RepositoryMetadataAccess() {});

0 commit comments

Comments
 (0)