Skip to content

Commit 1fb7b4d

Browse files
mp911demipo256
authored andcommitted
Add RepositoryMetadataAccess.
Repositories that detect fragment implementations implementing RepositoryMetadataAccess enable metadata exposure regardless of the exposeMetadata flag. See: spring-projects#3090 Original Pull Request: spring-projects#3145
1 parent 9ec5341 commit 1fb7b4d

File tree

3 files changed

+76
-5
lines changed

3 files changed

+76
-5
lines changed

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

+21-5
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,16 @@ public RepositoryFactorySupport() {
127127
* retrieval via the {@code RepositoryMethodContext} class. This is useful if an advised object needs to obtain
128128
* repository information.
129129
* <p>
130-
* Default is {@literal "false"}, in order to avoid unnecessary extra interception. This means that no guarantees are provided
131-
* that {@code RepositoryMethodContext} access will work consistently within any method of the advised object.
132-
*
133-
* @since 3.4.0
130+
* Default is {@literal "false"}, in order to avoid unnecessary extra interception. This means that no guarantees are
131+
* provided that {@code RepositoryMethodContext} access will work consistently within any method of the advised
132+
* object.
133+
* <p>
134+
* Repository method metadata is also exposed if implementations within the {@link RepositoryFragments repository
135+
* composition} implement {@link RepositoryMetadataAccess}.
136+
*
137+
* @since 3.4
138+
* @see RepositoryMethodContext
139+
* @see RepositoryMetadataAccess
134140
*/
135141
public void setExposeMetadata(boolean exposeMetadata) {
136142
this.exposeMetadata = exposeMetadata;
@@ -345,7 +351,7 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
345351
result.addAdvice(new MethodInvocationValidator());
346352
}
347353

348-
if (this.exposeMetadata) {
354+
if (this.exposeMetadata || shouldExposeMetadata(fragments)) {
349355
result.addAdvice(new ExposeMetadataInterceptor(metadata));
350356
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
351357
}
@@ -616,6 +622,16 @@ private Lazy<ProjectionFactory> createProjectionFactory() {
616622
return Lazy.of(() -> getProjectionFactory(this.classLoader, this.beanFactory));
617623
}
618624

625+
private static boolean shouldExposeMetadata(RepositoryFragments fragments) {
626+
627+
for (RepositoryFragment<?> fragment : fragments) {
628+
if (fragment.getImplementation().filter(RepositoryMetadataAccess.class::isInstance).isPresent()) {
629+
return true;
630+
}
631+
}
632+
return false;
633+
}
634+
619635
/**
620636
* Method interceptor that calls methods on the {@link RepositoryComposition}.
621637
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
/**
19+
* Marker for repository fragment implementation that intend to access repository method invocation metadata.
20+
* <p>
21+
* Note that this is a marker interface in the style of {@link java.io.Serializable}, semantically applying to a
22+
* fragment implementation class rather. In other words, this marker applies to a particular repository composition that
23+
* enables metadata access for the repository proxy when the composition contain fragments implementing this interface.
24+
* <p>
25+
* Ideally, in a repository composition only the fragment implementation uses this interface while the fragment
26+
* interface does not.
27+
*
28+
* @author Mark Paluch
29+
* @since 3.4
30+
* @see RepositoryMethodContext
31+
*/
32+
public interface RepositoryMetadataAccess {
33+
34+
}

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

+21
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,27 @@ record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocati
271271
assertThat(metadata.methodInvocation().getMethod().getName()).isEqualTo("findMetadataByLastname");
272272
}
273273

274+
@Test // GH-3090
275+
void capturesRepositoryMetadataWithMetadataAccess() {
276+
277+
record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocation) {
278+
}
279+
280+
when(factory.queryOne.execute(any(Object[].class)))
281+
.then(invocation -> new Metadata(RepositoryMethodContext.currentMethod(),
282+
ExposeInvocationInterceptor.currentInvocation()));
283+
284+
var repository = factory.getRepository(ObjectRepository.class, new RepositoryMetadataAccess() {});
285+
var metadataByLastname = repository.findMetadataByLastname();
286+
287+
assertThat(metadataByLastname).isInstanceOf(Metadata.class);
288+
289+
Metadata metadata = (Metadata) metadataByLastname;
290+
assertThat(metadata.context().getMethod().getName()).isEqualTo("findMetadataByLastname");
291+
assertThat(metadata.context().getRepository().getDomainType()).isEqualTo(Object.class);
292+
assertThat(metadata.methodInvocation().getMethod().getName()).isEqualTo("findMetadataByLastname");
293+
}
294+
274295
@Test // DATACMNS-509, DATACMNS-1764
275296
void convertsWithSameElementType() {
276297

0 commit comments

Comments
 (0)