DATACMNS-1108 - Add repository configuration infrastructure.

Add config infrastructure for ReactiveQueryMethodEvaluationContextProvider.

Original Pull Request: #454
This commit is contained in:
Mark Paluch
2020-06-30 11:13:04 +02:00
committed by Christoph Strobl
parent edcf1a0562
commit 0671862ed8
11 changed files with 197 additions and 52 deletions

View File

@@ -19,8 +19,11 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import org.reactivestreams.Publisher;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.util.ReactiveWrapperConverters;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.util.ClassUtils;
@@ -56,6 +59,20 @@ public abstract class ReactiveRepositoryFactorySupport extends RepositoryFactory
}
}
/**
* Sets the {@link QueryMethodEvaluationContextProvider} to be used to evaluate SpEL expressions in manually defined
* queries.
*
* @param evaluationContextProvider can be {@literal null}, defaults to
* {@link ReactiveQueryMethodEvaluationContextProvider#INSTANCE}.
*/
@Override
public void setEvaluationContextProvider(QueryMethodEvaluationContextProvider evaluationContextProvider) {
super.setEvaluationContextProvider(
evaluationContextProvider == null ? ReactiveQueryMethodEvaluationContextProvider.DEFAULT
: evaluationContextProvider);
}
/**
* We need to make sure that the necessary conversion libraries are in place if the repository interface uses RxJava 1
* types.

View File

@@ -182,11 +182,23 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
this.beanFactory = beanFactory;
if (!this.evaluationContextProvider.isPresent() && ListableBeanFactory.class.isInstance(beanFactory)) {
this.evaluationContextProvider = Optional
.of(new ExtensionAwareQueryMethodEvaluationContextProvider((ListableBeanFactory) beanFactory));
this.evaluationContextProvider = createDefaultQueryMethodEvaluationContextProvider(
(ListableBeanFactory) beanFactory);
}
}
/**
* Create a default {@link QueryMethodEvaluationContextProvider} (or subclass) from {@link ListableBeanFactory}.
*
* @param beanFactory the bean factory to use.
* @return the default instance. May be {@link Optional#empty()}.
* @since 2.4
*/
protected Optional<QueryMethodEvaluationContextProvider> createDefaultQueryMethodEvaluationContextProvider(
ListableBeanFactory beanFactory) {
return Optional.of(new ExtensionAwareQueryMethodEvaluationContextProvider(beanFactory));
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)

View File

@@ -30,7 +30,7 @@ import org.springframework.expression.EvaluationContext;
*/
public interface QueryMethodEvaluationContextProvider {
static QueryMethodEvaluationContextProvider DEFAULT = new ExtensionAwareQueryMethodEvaluationContextProvider(
QueryMethodEvaluationContextProvider DEFAULT = new ExtensionAwareQueryMethodEvaluationContextProvider(
Collections.emptyList());
/**

View File

@@ -68,15 +68,50 @@ public class ReactiveExtensionAwareQueryMethodEvaluationContextProvider
this.delegate = new ReactiveExtensionAwareEvaluationContextProvider(extensions);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryMethodEvaluationContextProvider#getEvaluationContext(org.springframework.data.repository.query.Parameters, java.lang.Object[])
*/
@Override
public <T extends Parameters<?, ?>> EvaluationContext getEvaluationContext(T parameters, Object[] parameterValues) {
EvaluationContext evaluationContext = delegate.getEvaluationContext(parameterValues);
if (evaluationContext instanceof StandardEvaluationContext) {
((StandardEvaluationContext) evaluationContext).setVariables(
ExtensionAwareQueryMethodEvaluationContextProvider.collectVariables(parameters, parameterValues));
}
return evaluationContext;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryMethodEvaluationContextProvider#getEvaluationContext(org.springframework.data.repository.query.Parameters, java.lang.Object[], org.springframework.data.spel.ExpressionDependencies)
*/
@Override
public <T extends Parameters<?, ?>> EvaluationContext getEvaluationContext(T parameters, Object[] parameterValues,
ExpressionDependencies dependencies) {
EvaluationContext evaluationContext = delegate.getEvaluationContext(parameterValues, dependencies);
if (evaluationContext instanceof StandardEvaluationContext) {
((StandardEvaluationContext) evaluationContext).setVariables(
ExtensionAwareQueryMethodEvaluationContextProvider.collectVariables(parameters, parameterValues));
}
return evaluationContext;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider#getEvaluationContext(org.springframework.data.repository.query.Parameters, java.lang.Object[])
*/
@Override
public <T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContext(T parameters,
public <T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters,
Object[] parameterValues) {
Mono<StandardEvaluationContext> evaluationContext = delegate.getEvaluationContext(parameterValues);
Mono<StandardEvaluationContext> evaluationContext = delegate.getEvaluationContextLater(parameterValues);
return evaluationContext
.doOnNext(it -> it.setVariables(
@@ -84,15 +119,16 @@ public class ReactiveExtensionAwareQueryMethodEvaluationContextProvider
.cast(EvaluationContext.class);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider#getEvaluationContext(org.springframework.data.repository.query.Parameters, java.lang.Object[], ExpressionDependencies)
* @see org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider#getEvaluationContextLater(org.springframework.data.repository.query.Parameters, java.lang.Object[], org.springframework.data.spel.ExpressionDependencies)
*/
@Override
public <T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContext(T parameters,
public <T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters,
Object[] parameterValues, ExpressionDependencies dependencies) {
Mono<StandardEvaluationContext> evaluationContext = delegate.getEvaluationContext(parameterValues, dependencies);
Mono<StandardEvaluationContext> evaluationContext = delegate.getEvaluationContextLater(parameterValues,
dependencies);
return evaluationContext
.doOnNext(it -> it.setVariables(

View File

@@ -17,33 +17,40 @@ package org.springframework.data.repository.query;
import reactor.core.publisher.Mono;
import java.util.Collections;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.expression.EvaluationContext;
/**
* Provides a way to access a centrally defined potentially shared {@link EvaluationContext}.
* Provides a way to access a centrally defined potentially shared {@link EvaluationContext} by considering
* {@link org.springframework.data.spel.spi.ReactiveEvaluationContextExtension}.
*
* @author Mark Paluch
* @since 2.4
*/
public interface ReactiveQueryMethodEvaluationContextProvider {
public interface ReactiveQueryMethodEvaluationContextProvider extends QueryMethodEvaluationContextProvider {
ReactiveQueryMethodEvaluationContextProvider DEFAULT = new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(
Collections.emptyList());
/**
* Returns an {@link EvaluationContext} built using the given {@link Parameters} and parameter values.
*
* @param parameters the {@link Parameters} instance obtained from the query method the context is built for.
* @param parameterValues the values for the parameters.
* @return
* @return a mono that emits exactly one {@link EvaluationContext}.
*/
<T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContext(T parameters, Object[] parameterValues);
<T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters,
Object[] parameterValues);
/**
* Returns an {@link EvaluationContext} built using the given {@link Parameters} and parameter values.
*
* @param parameters the {@link Parameters} instance obtained from the query method the context is built for.
* @param parameterValues the values for the parameters.
* @return
* @return a mono that emits exactly one {@link EvaluationContext}.
*/
<T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContext(T parameters, Object[] parameterValues,
<T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters, Object[] parameterValues,
ExpressionDependencies dependencies);
}

View File

@@ -18,7 +18,9 @@ package org.springframework.data.spel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import org.springframework.data.util.Streamable;
@@ -28,6 +30,7 @@ import org.springframework.expression.spel.ast.CompoundExpression;
import org.springframework.expression.spel.ast.MethodReference;
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
@@ -46,6 +49,15 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
this.dependencies = dependencies;
}
/**
* Return an empty {@link ExpressionDependencies} object.
*
* @return the empty dependencies.
*/
public static ExpressionDependencies empty() {
return EMPTY;
}
/**
* Discover all expression dependencies that are referenced in the {@link SpelNode expression root}.
*
@@ -53,7 +65,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
* @return a set of {@link ExpressionDependencies}.
*/
public static ExpressionDependencies discover(Expression expression) {
return expression instanceof SpelExpression ? discover(((SpelExpression) expression).getAST(), true) : EMPTY;
return expression instanceof SpelExpression ? discover(((SpelExpression) expression).getAST(), true) : empty();
}
/**
@@ -74,7 +86,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
}
});
return new ExpressionDependencies(Collections.unmodifiableList(dependencies));
return new ExpressionDependencies(dependencies);
}
private static void collectDependencies(SpelNode node, int compoundPosition,
@@ -94,7 +106,25 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
}
}
/*
/**
* Create new {@link ExpressionDependencies} that contains all dependencies from this object and {@code other}. The
* merged dependencies are guaranteed to not contain duplicates.
*
* @param other the other {@link ExpressionDependencies} object.
* @return new merged {@link ExpressionDependencies} object.
*/
public ExpressionDependencies mergeWith(ExpressionDependencies other) {
Assert.notNull(other, "Other ExpressionDependencies must not be null");
Set<ExpressionDependency> dependencySet = new LinkedHashSet<>(this.dependencies.size() + other.dependencies.size());
dependencySet.addAll(this.dependencies);
dependencySet.addAll(other.dependencies);
return new ExpressionDependencies(new ArrayList<>(dependencySet));
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@@ -103,7 +133,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
return this.dependencies.iterator();
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@@ -119,7 +149,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
return ObjectUtils.nullSafeEquals(dependencies, that.dependencies);
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@@ -196,7 +226,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
return symbol;
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@@ -218,7 +248,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
return ObjectUtils.nullSafeEquals(symbol, that.symbol);
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@@ -230,7 +260,7 @@ public class ExpressionDependencies implements Streamable<ExpressionDependencies
return result;
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/

View File

@@ -99,7 +99,8 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
this.extensions = extensions;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.spel.EvaluationContextProvider#getEvaluationContext(Object)
*/
@Override
@@ -107,8 +108,9 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
return doGetEvaluationContext(rootObject, getExtensions(it -> true));
}
/* (non-Javadoc)
* @see org.springframework.data.spel.EvaluationContextProvider#getEvaluationContext(Object, Collection, ExpressionDependencies)
/*
* (non-Javadoc)
* @see org.springframework.data.spel.EvaluationContextProvider#getEvaluationContext(Object, ExpressionDependencies)
*/
@Override
public StandardEvaluationContext getEvaluationContext(Object rootObject, ExpressionDependencies dependencies) {

View File

@@ -25,16 +25,15 @@ import org.springframework.expression.EvaluationContext;
* @author Mark Paluch
* @since 2.4
*/
@FunctionalInterface
public interface ReactiveEvaluationContextProvider {
public interface ReactiveEvaluationContextProvider extends EvaluationContextProvider {
/**
* Returns an {@link EvaluationContext} built using the given parameter values.
*
* @param rootObject the root object to set in the {@link EvaluationContext}.
* @return mono emitting the {@link EvaluationContext}.
* @return a mono that emits exactly one {@link EvaluationContext}.
*/
Mono<? extends EvaluationContext> getEvaluationContext(Object rootObject);
Mono<? extends EvaluationContext> getEvaluationContextLater(Object rootObject);
/**
* Returns a tailored {@link EvaluationContext} built using the given parameter values and considering
@@ -44,11 +43,11 @@ public interface ReactiveEvaluationContextProvider {
*
* @param rootObject the root object to set in the {@link EvaluationContext}.
* @param dependencies the requested expression dependencies to be available.
* @return mono emitting the {@link EvaluationContext}.
* @return a mono that emits exactly one {@link EvaluationContext}.
* @since 2.4
*/
default Mono<? extends EvaluationContext> getEvaluationContext(Object rootObject,
default Mono<? extends EvaluationContext> getEvaluationContextLater(Object rootObject,
ExpressionDependencies dependencies) {
return getEvaluationContext(rootObject);
return getEvaluationContextLater(rootObject);
}
}

View File

@@ -27,7 +27,6 @@ import org.springframework.core.ResolvableType;
import org.springframework.data.spel.spi.EvaluationContextExtension;
import org.springframework.data.spel.spi.ExtensionIdAware;
import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@@ -37,6 +36,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
* {@link ReactiveEvaluationContextExtension} and {@link EvaluationContextExtension} instances.
*
* @author Mark Paluch
* @since 2.4
*/
public class ReactiveExtensionAwareEvaluationContextProvider implements ReactiveEvaluationContextProvider {
@@ -49,32 +49,67 @@ public class ReactiveExtensionAwareEvaluationContextProvider implements Reactive
evaluationContextProvider = new ExtensionAwareEvaluationContextProvider();
}
/**
* Create a new {@link ReactiveExtensionAwareEvaluationContextProvider} with extensions looked up lazily from the
* given {@link ListableBeanFactory}.
*
* @param beanFactory the {@link ListableBeanFactory} to lookup extensions from.
*/
public ReactiveExtensionAwareEvaluationContextProvider(ListableBeanFactory beanFactory) {
evaluationContextProvider = new ExtensionAwareEvaluationContextProvider(beanFactory);
}
/**
* Creates a new {@link ReactiveExtensionAwareEvaluationContextProvider} for the given
* {@link EvaluationContextExtension}s.
*
* @param extensions must not be {@literal null}.
*/
public ReactiveExtensionAwareEvaluationContextProvider(Collection<? extends ExtensionIdAware> extensions) {
evaluationContextProvider = new ExtensionAwareEvaluationContextProvider(extensions);
}
public ReactiveExtensionAwareEvaluationContextProvider(
Lazy<? extends Collection<? extends ExtensionIdAware>> extensions) {
evaluationContextProvider = new ExtensionAwareEvaluationContextProvider(extensions);
/*
* (non-Javadoc)
* @see org.springframework.data.spel.EvaluationContextProvider#getEvaluationContext(Object)
*/
@Override
public EvaluationContext getEvaluationContext(Object rootObject) {
return evaluationContextProvider.getEvaluationContext(rootObject);
}
/*
* (non-Javadoc)
* @see org.springframework.data.spel.EvaluationContextProvider#getEvaluationContext(java.lang.Object, org.springframework.data.spel.ExpressionDependencies)
*/
@Override
public Mono<StandardEvaluationContext> getEvaluationContext(Object rootObject) {
public EvaluationContext getEvaluationContext(Object rootObject, ExpressionDependencies dependencies) {
return evaluationContextProvider.getEvaluationContext(rootObject, dependencies);
}
/*
* (non-Javadoc)
* @see org.springframework.data.spel.ReactiveEvaluationContextProvider#getEvaluationContextLater(java.lang.Object)
*/
@Override
public Mono<StandardEvaluationContext> getEvaluationContextLater(Object rootObject) {
return getExtensions(it -> true) //
.map(it -> evaluationContextProvider.doGetEvaluationContext(rootObject, it));
}
/*
* (non-Javadoc)
* @see org.springframework.data.spel.ReactiveEvaluationContextProvider#getEvaluationContextLater(java.lang.Object, org.springframework.data.spel.ExpressionDependencies)
*/
@Override
public Mono<StandardEvaluationContext> getEvaluationContext(Object rootObject, ExpressionDependencies dependencies) {
public Mono<StandardEvaluationContext> getEvaluationContextLater(Object rootObject,
ExpressionDependencies dependencies) {
return getExtensions(it -> dependencies.stream().anyMatch(it::provides)) //
.map(it -> evaluationContextProvider.doGetEvaluationContext(rootObject, it));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Mono<List<EvaluationContextExtension>> getExtensions(
Predicate<EvaluationContextExtensionInformation> extensionFilter) {
@@ -83,6 +118,7 @@ public class ReactiveExtensionAwareEvaluationContextProvider implements Reactive
return Flux.fromIterable(extensions).concatMap(it -> {
if (it instanceof EvaluationContextExtension) {
EvaluationContextExtension extension = (EvaluationContextExtension) it;
EvaluationContextExtensionInformation information = evaluationContextProvider.getOrCreateInformation(extension);
@@ -114,13 +150,10 @@ public class ReactiveExtensionAwareEvaluationContextProvider implements Reactive
}).collectList();
}
private ResolvableType getExtensionType(ExtensionIdAware extensionCandidate) {
private static ResolvableType getExtensionType(ExtensionIdAware extensionCandidate) {
ResolvableType extensionType = ResolvableType
return ResolvableType
.forMethodReturnType(ReflectionUtils.findRequiredMethod(extensionCandidate.getClass(), "getExtension"))
.getGeneric(0);
return extensionType;
}
}