Add RepositoryMetadataAccess.

Repositories that detect fragment implementations implementing RepositoryMetadataAccess enable metadata exposure regardless of the exposeMetadata flag.

See: #3090
Original Pull Request: #3145
This commit is contained in:
Mark Paluch
2024-08-29 13:55:19 +02:00
committed by Christoph Strobl
parent b99db0cf01
commit f7895eb306
3 changed files with 76 additions and 5 deletions

View File

@@ -127,10 +127,16 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
* retrieval via the {@code RepositoryMethodContext} class. This is useful if an advised object needs to obtain
* repository information.
* <p>
* Default is {@literal "false"}, in order to avoid unnecessary extra interception. This means that no guarantees are provided
* that {@code RepositoryMethodContext} access will work consistently within any method of the advised object.
*
* @since 3.4.0
* Default is {@literal "false"}, in order to avoid unnecessary extra interception. This means that no guarantees are
* provided that {@code RepositoryMethodContext} access will work consistently within any method of the advised
* object.
* <p>
* Repository method metadata is also exposed if implementations within the {@link RepositoryFragments repository
* composition} implement {@link RepositoryMetadataAccess}.
*
* @since 3.4
* @see RepositoryMethodContext
* @see RepositoryMetadataAccess
*/
public void setExposeMetadata(boolean exposeMetadata) {
this.exposeMetadata = exposeMetadata;
@@ -345,7 +351,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
result.addAdvice(new MethodInvocationValidator());
}
if (this.exposeMetadata) {
if (this.exposeMetadata || shouldExposeMetadata(fragments)) {
result.addAdvice(new ExposeMetadataInterceptor(metadata));
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
}
@@ -616,6 +622,16 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware,
return Lazy.of(() -> getProjectionFactory(this.classLoader, this.beanFactory));
}
private static boolean shouldExposeMetadata(RepositoryFragments fragments) {
for (RepositoryFragment<?> fragment : fragments) {
if (fragment.getImplementation().filter(RepositoryMetadataAccess.class::isInstance).isPresent()) {
return true;
}
}
return false;
}
/**
* Method interceptor that calls methods on the {@link RepositoryComposition}.
*

View File

@@ -0,0 +1,34 @@
/*
* 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.support;
/**
* Marker for repository fragment implementation that intend to access repository method invocation metadata.
* <p>
* Note that this is a marker interface in the style of {@link java.io.Serializable}, semantically applying to a
* fragment implementation class rather. In other words, this marker applies to a particular repository composition that
* enables metadata access for the repository proxy when the composition contain fragments implementing this interface.
* <p>
* Ideally, in a repository composition only the fragment implementation uses this interface while the fragment
* interface does not.
*
* @author Mark Paluch
* @since 3.4
* @see RepositoryMethodContext
*/
public interface RepositoryMetadataAccess {
}

View File

@@ -271,6 +271,27 @@ class RepositoryFactorySupportUnitTests {
assertThat(metadata.methodInvocation().getMethod().getName()).isEqualTo("findMetadataByLastname");
}
@Test // GH-3090
void capturesRepositoryMetadataWithMetadataAccess() {
record Metadata(RepositoryMethodContext context, MethodInvocation methodInvocation) {
}
when(factory.queryOne.execute(any(Object[].class)))
.then(invocation -> new Metadata(RepositoryMethodContext.currentMethod(),
ExposeInvocationInterceptor.currentInvocation()));
var repository = factory.getRepository(ObjectRepository.class, new RepositoryMetadataAccess() {});
var metadataByLastname = repository.findMetadataByLastname();
assertThat(metadataByLastname).isInstanceOf(Metadata.class);
Metadata metadata = (Metadata) metadataByLastname;
assertThat(metadata.context().getMethod().getName()).isEqualTo("findMetadataByLastname");
assertThat(metadata.context().getRepository().getDomainType()).isEqualTo(Object.class);
assertThat(metadata.methodInvocation().getMethod().getName()).isEqualTo("findMetadataByLastname");
}
@Test // DATACMNS-509, DATACMNS-1764
void convertsWithSameElementType() {