Skip to content

Commit 323aa60

Browse files
odrotbohmmp911de
authored andcommitted
Move RepositoryMethodContext to repository.core package.
RepositoryMethodContext are now made available for dependency injection via RepositoryConfigurationExtensionSupport.registerBeansForRoot(…). Moved RMC into repository.core package (previously repository.core.support) and only expose factory methods on DefaultRepositoryMethodContext. DRMC also exposes a injection proxy lookup method that creates a proxy equipped with a TargetSource delegating to DRMC.getInstance() (previously ….getContext()). An additional, static DRMC.forMethod(…) allows the creation of a default instance for testing purposes. Rename getRepository() to getMetadata() on RMC. Fixes #3175. Original pull request: #3176
1 parent 55afe84 commit 323aa60

File tree

8 files changed

+207
-108
lines changed

8 files changed

+207
-108
lines changed

src/main/java/org/springframework/data/repository/config/RepositoryConfigurationExtensionSupport.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@
3131
import org.springframework.beans.factory.support.AbstractBeanDefinition;
3232
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3333
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
34+
import org.springframework.beans.factory.support.RootBeanDefinition;
3435
import org.springframework.core.annotation.AnnotationUtils;
3536
import org.springframework.core.io.ResourceLoader;
3637
import org.springframework.core.log.LogMessage;
3738
import org.springframework.dao.InvalidDataAccessApiUsageException;
3839
import org.springframework.data.repository.core.RepositoryMetadata;
40+
import org.springframework.data.repository.core.RepositoryMethodContext;
3941
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
42+
import org.springframework.data.repository.core.support.DefaultRepositoryMethodContext;
4043
import org.springframework.lang.Nullable;
4144
import org.springframework.util.Assert;
4245
import org.springframework.util.StringUtils;
@@ -109,7 +112,13 @@ public String getDefaultNamedQueryLocation() {
109112

110113
@Override
111114
public void registerBeansForRoot(BeanDefinitionRegistry registry,
112-
RepositoryConfigurationSource configurationSource) {}
115+
RepositoryConfigurationSource configurationSource) {
116+
117+
// A proxy RepositoryMethodContext for dependency injection
118+
registerIfNotAlreadyRegistered(
119+
() -> new RootBeanDefinition(RepositoryMethodContext.class, DefaultRepositoryMethodContext::getInjectionProxy),
120+
registry, "repositoryMethodContextFactory", configurationSource);
121+
}
113122

114123
/**
115124
* Returns the prefix of the module to be used to create the default location for Spring Data named queries.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 java.lang.reflect.Method;
19+
20+
/**
21+
* Interface containing methods and value objects to obtain information about the current repository method invocation.
22+
* <p>
23+
* The {@link #currentMethod()} 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.
26+
* <p>
27+
* Spring Data's framework does not expose method metadata by default, as there is a performance cost in doing so.
28+
* <p>
29+
* The functionality in this class might be used by a target object that needed access to resources on the invocation.
30+
* However, this approach should not be used when there is a reasonable alternative, as it makes application code
31+
* dependent on usage in particular.
32+
*
33+
* @author Christoph Strobl
34+
* @author Mark Paluch
35+
* @author Oliver Drotbohm
36+
* @since 3.4.0
37+
*/
38+
public interface RepositoryMethodContext {
39+
40+
/**
41+
* Returns the metadata for the repository.
42+
*
43+
* @return the repository metadata, will never be {@literal null}.
44+
*/
45+
RepositoryMetadata getMetadata();
46+
47+
/**
48+
* Returns the current method that is being invoked.
49+
* <p>
50+
* The method object represents the method as being invoked on the repository interface. It doesn't match the backing
51+
* repository implementation in case the method invocation is delegated to an implementation method.
52+
*
53+
* @return the current method, will never be {@literal null}..
54+
*/
55+
Method getMethod();
56+
}

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,22 @@
1717

1818
import java.lang.reflect.Method;
1919

20+
import org.springframework.aop.framework.ProxyFactory;
2021
import org.springframework.core.NamedThreadLocal;
2122
import org.springframework.data.repository.core.RepositoryMetadata;
23+
import org.springframework.data.repository.core.RepositoryMethodContext;
2224
import org.springframework.lang.Nullable;
25+
import org.springframework.util.Assert;
2326

2427
/**
2528
* Class containing value objects providing information about the current repository method invocation.
2629
*
2730
* @author Christoph Strobl
2831
* @author Mark Paluch
32+
* @author Oliver Drotbohm
2933
* @since 3.4.0
3034
*/
31-
class DefaultRepositoryMethodContext implements RepositoryMethodContext {
35+
public class DefaultRepositoryMethodContext implements RepositoryMethodContext {
3236

3337
/**
3438
* ThreadLocal holder for repository method associated with this thread. Will contain {@code null} unless the
@@ -46,15 +50,41 @@ class DefaultRepositoryMethodContext implements RepositoryMethodContext {
4650
this.method = method;
4751
}
4852

53+
/**
54+
* Creates a new {@link RepositoryMethodContext} for the given {@link Method}.
55+
*
56+
* @param method must not be {@literal null}.
57+
* @return will never be {@literal null}.
58+
*/
59+
public static RepositoryMethodContext forMethod(Method method) {
60+
61+
Assert.notNull(method, "Method must not be null!");
62+
63+
return new DefaultRepositoryMethodContext(AbstractRepositoryMetadata.getMetadata(method.getDeclaringClass()),
64+
method);
65+
}
66+
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+
4978
@Nullable
50-
static RepositoryMethodContext getMetadata() {
79+
static RepositoryMethodContext getInstance() {
5180
return currentMethod.get();
5281
}
5382

5483
@Nullable
5584
static RepositoryMethodContext setMetadata(@Nullable RepositoryMethodContext metadata) {
5685

5786
RepositoryMethodContext old = currentMethod.get();
87+
5888
if (metadata != null) {
5989
currentMethod.set(metadata);
6090
} else {
@@ -65,13 +95,12 @@ static RepositoryMethodContext setMetadata(@Nullable RepositoryMethodContext met
6595
}
6696

6797
@Override
68-
public RepositoryMetadata getRepository() {
98+
public RepositoryMetadata getMetadata() {
6999
return repositoryMetadata;
70100
}
71101

72102
@Override
73103
public Method getMethod() {
74104
return method;
75105
}
76-
77106
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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.util.function.Supplier;
19+
20+
import org.springframework.aop.TargetSource;
21+
import org.springframework.lang.NonNull;
22+
import org.springframework.lang.Nullable;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* A {@link TargetSource}, that will re-obtain an instance using the configured supplier.
27+
*
28+
* @author Oliver Drotbohm
29+
* @since 3.4.0
30+
*/
31+
class DynamicLookupTargetSource<T> implements TargetSource {
32+
33+
private final Class<T> type;
34+
private final Supplier<? extends T> supplier;
35+
36+
/**
37+
* Creates a new {@link DynamicLookupTargetSource} for the given type and {@link Supplier}.
38+
*
39+
* @param type must not be {@literal null}.
40+
* @param supplier must not be {@literal null}.
41+
*/
42+
public DynamicLookupTargetSource(Class<T> type, Supplier<? extends T> supplier) {
43+
44+
Assert.notNull(type, "Type must not be null!");
45+
Assert.notNull(supplier, "Supplier must not be null!");
46+
47+
this.type = type;
48+
this.supplier = supplier;
49+
}
50+
51+
/*
52+
* (non-Javadoc)
53+
* @see org.springframework.aop.TargetSource#isStatic()
54+
*/
55+
@Override
56+
public boolean isStatic() {
57+
return false;
58+
}
59+
60+
/*
61+
* (non-Javadoc)
62+
* @see org.springframework.aop.TargetSource#getTarget()
63+
*/
64+
@Override
65+
@Nullable
66+
public Object getTarget() throws Exception {
67+
return supplier.get();
68+
}
69+
70+
/*
71+
* (non-Javadoc)
72+
* @see org.springframework.aop.TargetSource#getTargetClass()
73+
*/
74+
@Override
75+
@NonNull
76+
public Class<?> getTargetClass() {
77+
return type;
78+
}
79+
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.springframework.data.repository.core.NamedQueries;
5757
import org.springframework.data.repository.core.RepositoryInformation;
5858
import org.springframework.data.repository.core.RepositoryMetadata;
59+
import org.springframework.data.repository.core.RepositoryMethodContext;
5960
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
6061
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.DefaultRepositoryInvocationMulticaster;
6162
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
@@ -772,15 +773,18 @@ public ExposeMetadataInterceptor(RepositoryMetadata repositoryMetadata) {
772773
public Object invoke(MethodInvocation invocation) throws Throwable {
773774

774775
RepositoryMethodContext oldMetadata = null;
776+
775777
try {
776-
oldMetadata = RepositoryMethodContext
777-
.setCurrentMetadata(new DefaultRepositoryMethodContext(repositoryMetadata, invocation.getMethod()));
778+
779+
oldMetadata = DefaultRepositoryMethodContext
780+
.setMetadata(new DefaultRepositoryMethodContext(repositoryMetadata, invocation.getMethod()));
781+
778782
return invocation.proceed();
783+
779784
} finally {
780-
RepositoryMethodContext.setCurrentMetadata(oldMetadata);
785+
DefaultRepositoryMethodContext.setMetadata(oldMetadata);
781786
}
782787
}
783-
784788
}
785789

786790
/**

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

Lines changed: 0 additions & 94 deletions
This file was deleted.

0 commit comments

Comments
 (0)