diff --git a/src/main/java/org/springframework/data/repository/aot/hint/RepositoryRuntimeHints.java b/src/main/java/org/springframework/data/repository/aot/hint/RepositoryRuntimeHints.java
index 5fafd13a1e..7658d9128f 100644
--- a/src/main/java/org/springframework/data/repository/aot/hint/RepositoryRuntimeHints.java
+++ b/src/main/java/org/springframework/data/repository/aot/hint/RepositoryRuntimeHints.java
@@ -18,11 +18,14 @@
import java.util.Arrays;
import java.util.Properties;
+import org.springframework.aop.SpringProxy;
+import org.springframework.aop.framework.Advised;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.BeanFactory;
+import org.springframework.core.DecoratingProxy;
import org.springframework.core.io.InputStreamSource;
import org.springframework.data.domain.Example;
import org.springframework.data.mapping.context.MappingContext;
@@ -99,5 +102,13 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader)
// annotated queries
hints.proxies().registerJdkProxy( //
TypeReference.of("org.springframework.data.annotation.QueryAnnotation"));
+
+ registerSpringProxy(TypeReference.of("org.springframework.data.repository.core.RepositoryMethodContext"), hints);
+ }
+
+ private static void registerSpringProxy(TypeReference type, RuntimeHints runtimeHints) {
+
+ runtimeHints.proxies().registerJdkProxy(type, TypeReference.of(SpringProxy.class),
+ TypeReference.of(Advised.class), TypeReference.of(DecoratingProxy.class));
}
}
diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationExtensionSupport.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationExtensionSupport.java
index ecd405442b..7efe44c9c1 100644
--- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationExtensionSupport.java
+++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationExtensionSupport.java
@@ -15,7 +15,8 @@
*/
package org.springframework.data.repository.config;
-import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.*;
+import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.GENERATED_BEAN_NAME_SEPARATOR;
+import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.generateBeanName;
import java.lang.annotation.Annotation;
import java.util.Collection;
@@ -31,15 +32,12 @@
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.log.LogMessage;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.repository.core.RepositoryMetadata;
-import org.springframework.data.repository.core.RepositoryMethodContext;
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
-import org.springframework.data.repository.core.support.DefaultRepositoryMethodContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -112,13 +110,7 @@ public String getDefaultNamedQueryLocation() {
@Override
public void registerBeansForRoot(BeanDefinitionRegistry registry,
- RepositoryConfigurationSource configurationSource) {
-
- // A proxy RepositoryMethodContext for dependency injection
- registerIfNotAlreadyRegistered(
- () -> new RootBeanDefinition(RepositoryMethodContext.class, DefaultRepositoryMethodContext::getInjectionProxy),
- registry, "repositoryMethodContextFactory", configurationSource);
- }
+ RepositoryConfigurationSource configurationSource) {}
/**
* Returns the prefix of the module to be used to create the default location for Spring Data named queries.
diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryMethodContext.java b/src/main/java/org/springframework/data/repository/core/RepositoryMethodContext.java
index da4b4da3de..12c7829ecf 100644
--- a/src/main/java/org/springframework/data/repository/core/RepositoryMethodContext.java
+++ b/src/main/java/org/springframework/data/repository/core/RepositoryMethodContext.java
@@ -20,9 +20,9 @@
/**
* Interface containing methods and value objects to obtain information about the current repository method invocation.
*
- * The {@link #currentMethod()} method is usable if the repository factory is configured to expose the current
- * repository method metadata (not the default). It returns the invoked repository method. Target objects or advice can
- * use this to make advised calls.
+ * The {@link #getMetadata()} method is usable if the repository factory is configured to expose the current repository
+ * method metadata (not the default). It returns the invoked repository method. Target objects or advice can use this to
+ * make advised calls.
*
* Spring Data's framework does not expose method metadata by default, as there is a performance cost in doing so.
*
@@ -50,7 +50,7 @@ public interface RepositoryMethodContext {
* The method object represents the method as being invoked on the repository interface. It doesn't match the backing
* repository implementation in case the method invocation is delegated to an implementation method.
*
- * @return the current method, will never be {@literal null}..
+ * @return the current method, will never be {@literal null}.
*/
Method getMethod();
}
diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryMethodContextHolder.java b/src/main/java/org/springframework/data/repository/core/RepositoryMethodContextHolder.java
new file mode 100644
index 0000000000..bf3fda56d9
--- /dev/null
+++ b/src/main/java/org/springframework/data/repository/core/RepositoryMethodContextHolder.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.repository.core;
+
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.lang.Nullable;
+
+/**
+ * @author Christoph Strobl
+ * @since 3.4.0
+ */
+public class RepositoryMethodContextHolder {
+
+ private static ContextProvider contextSupplier;
+
+ static {
+ contextSupplier = new ThreadLocalContextProvider();
+ }
+
+ @Nullable
+ public static RepositoryMethodContext setContext(@Nullable RepositoryMethodContext context) {
+ return contextSupplier.set(context);
+ }
+
+ @Nullable
+ public static RepositoryMethodContext current() {
+ return contextSupplier.get();
+ }
+
+ public static void clearContext() {
+ contextSupplier.clear();
+ }
+
+ interface ContextProvider {
+
+ @Nullable
+ RepositoryMethodContext get();
+
+ @Nullable
+ RepositoryMethodContext set(@Nullable RepositoryMethodContext context);
+
+ void clear();
+ }
+
+ static class ThreadLocalContextProvider implements ContextProvider {
+
+ /**
+ * ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
+ * "exposeMetadata" property on the controlling repository factory configuration has been set to "true".
+ */
+ private static final ThreadLocal currentMethod = new NamedThreadLocal<>(
+ "Current Repository Method");
+
+ @Override
+ @Nullable
+ public RepositoryMethodContext get() {
+ return currentMethod.get();
+ }
+
+ public void clear() {
+ currentMethod.remove();
+ }
+
+ @Override
+ @Nullable
+ public RepositoryMethodContext set(@Nullable RepositoryMethodContext context) {
+
+ RepositoryMethodContext old = currentMethod.get();
+
+ if (context != null) {
+ currentMethod.set(context);
+ } else {
+ currentMethod.remove();
+ }
+
+ return old;
+ }
+ }
+}
diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMethodContext.java b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMethodContext.java
index cf47441a71..3d02cc2883 100644
--- a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMethodContext.java
+++ b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMethodContext.java
@@ -17,11 +17,8 @@
import java.lang.reflect.Method;
-import org.springframework.aop.framework.ProxyFactory;
-import org.springframework.core.NamedThreadLocal;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.RepositoryMethodContext;
-import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -34,13 +31,6 @@
*/
public class DefaultRepositoryMethodContext implements RepositoryMethodContext {
- /**
- * ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
- * "exposeMetadata" property on the controlling repository factory configuration has been set to "true".
- */
- private static final ThreadLocal currentMethod = new NamedThreadLocal<>(
- "Current Repository Method");
-
private final RepositoryMetadata repositoryMetadata;
private final Method method;
@@ -64,36 +54,6 @@ public static RepositoryMethodContext forMethod(Method method) {
method);
}
- /**
- * Creates a proxy {@link RepositoryMethodContext} instance suitable for dependency injection.
- *
- * @return will never be {@literal null}.
- */
- public static RepositoryMethodContext getInjectionProxy() {
-
- return ProxyFactory.getProxy(RepositoryMethodContext.class,
- new DynamicLookupTargetSource<>(RepositoryMethodContext.class, () -> getInstance()));
- }
-
- @Nullable
- static RepositoryMethodContext getInstance() {
- return currentMethod.get();
- }
-
- @Nullable
- static RepositoryMethodContext setMetadata(@Nullable RepositoryMethodContext metadata) {
-
- RepositoryMethodContext old = currentMethod.get();
-
- if (metadata != null) {
- currentMethod.set(metadata);
- } else {
- currentMethod.remove();
- }
-
- return old;
- }
-
@Override
public RepositoryMetadata getMetadata() {
return repositoryMetadata;
diff --git a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
index b7b0e48d5c..8730942c99 100644
--- a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
+++ b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
@@ -57,6 +57,7 @@
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.RepositoryMethodContext;
+import org.springframework.data.repository.core.RepositoryMethodContextHolder;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.DefaultRepositoryInvocationMulticaster;
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
@@ -776,13 +777,13 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
try {
- oldMetadata = DefaultRepositoryMethodContext
- .setMetadata(new DefaultRepositoryMethodContext(repositoryMetadata, invocation.getMethod()));
+ oldMetadata = RepositoryMethodContextHolder
+ .setContext(new DefaultRepositoryMethodContext(repositoryMetadata, invocation.getMethod()));
return invocation.proceed();
} finally {
- DefaultRepositoryMethodContext.setMetadata(oldMetadata);
+ RepositoryMethodContextHolder.setContext(oldMetadata);
}
}
}
diff --git a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java
index a2be6d322e..ebdaddef42 100644
--- a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java
+++ b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java
@@ -276,24 +276,6 @@ void considersGenericLength() {
assertThat(it.getGeneric(1).resolve()).isEqualTo(Person.class);
}
- @Test // GH-3175
- void registersRepositoryMethodContextForInjection() {
-
- var environment = new StandardEnvironment();
- var context = new GenericApplicationContext();
- context.registerBean("fragment", MyFragmentImpl.class);
-
- RepositoryConfigurationSource configSource = new AnnotationRepositoryConfigurationSource(
- AnnotationMetadata.introspect(TestConfig.class), EnableRepositories.class, context, environment,
- context.getDefaultListableBeanFactory(), new AnnotationBeanNameGenerator());
-
- var delegate = new RepositoryConfigurationDelegate(configSource, context, environment);
-
- delegate.registerRepositoriesIn(context, extension);
-
- assertThat(context.containsBeanDefinition("repositoryMethodContextFactory")).isTrue();
- }
-
private static ListableBeanFactory assertLazyRepositoryBeanSetup(Class> configClass) {
var context = new AnnotationConfigApplicationContext(configClass);
diff --git a/src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java
index 6823989c94..6de556045d 100755
--- a/src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java
+++ b/src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java
@@ -57,6 +57,8 @@
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
+import org.springframework.data.repository.core.RepositoryMethodContext;
+import org.springframework.data.repository.core.RepositoryMethodContextHolder;
import org.springframework.data.repository.core.support.DummyRepositoryFactory.MyRepositoryQuery;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocation;
@@ -254,7 +256,7 @@ void capturesRepositoryMetadata() {
record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocation) {}
when(factory.queryOne.execute(any(Object[].class)))
- .then(invocation -> new Metadata(DefaultRepositoryMethodContext.getInstance(),
+ .then(invocation -> new Metadata(RepositoryMethodContextHolder.current(),
ExposeInvocationInterceptor.currentInvocation()));
factory.setExposeMetadata(true);
@@ -277,7 +279,7 @@ record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocati
}
when(factory.queryOne.execute(any(Object[].class)))
- .then(invocation -> new Metadata(RepositoryMethodContext.currentMethod(),
+ .then(invocation -> new Metadata(RepositoryMethodContextHolder.current(),
ExposeInvocationInterceptor.currentInvocation()));
var repository = factory.getRepository(ObjectRepository.class, new RepositoryMetadataAccess() {});
@@ -287,7 +289,7 @@ record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocati
Metadata metadata = (Metadata) metadataByLastname;
assertThat(metadata.context().getMethod().getName()).isEqualTo("findMetadataByLastname");
- assertThat(metadata.context().getRepository().getDomainType()).isEqualTo(Object.class);
+ assertThat(metadata.context().getMetadata().getDomainType()).isEqualTo(Object.class);
assertThat(metadata.methodInvocation().getMethod().getName()).isEqualTo("findMetadataByLastname");
}