package org.springframework.data.projection;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
class SpelEvaluatingMethodInterceptor implements MethodInterceptor {
private static final ParserContext PARSER_CONTEXT = new TemplateParserContext();
private final EvaluationContext evaluationContext;
private final MethodInterceptor delegate;
private final Map<Integer, Expression> expressions;
private final Object target;
public SpelEvaluatingMethodInterceptor(MethodInterceptor delegate, Object target, @Nullable BeanFactory beanFactory,
SpelExpressionParser parser, Class<?> targetInterface) {
Assert.notNull(delegate, "Delegate MethodInterceptor must not be null!");
Assert.notNull(target, "Target object must not be null!");
Assert.notNull(parser, "SpelExpressionParser must not be null!");
Assert.notNull(targetInterface, "Target interface must not be null!");
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
if (target instanceof Map) {
evaluationContext.addPropertyAccessor(new MapAccessor());
}
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
this.expressions = potentiallyCreateExpressionsForMethodsOnTargetInterface(parser, targetInterface);
this.evaluationContext = evaluationContext;
this.delegate = delegate;
this.target = target;
}
private static Map<Integer, Expression> potentiallyCreateExpressionsForMethodsOnTargetInterface(
SpelExpressionParser parser, Class<?> targetInterface) {
Map<Integer, Expression> expressions = new HashMap<>();
for (Method method : targetInterface.getMethods()) {
if (!method.isAnnotationPresent(Value.class)) {
continue;
}
Value value = method.getAnnotation(Value.class);
if (!StringUtils.hasText(value.value())) {
throw new IllegalStateException(String.format("@Value annotation on %s contains empty expression!", method));
}
expressions.put(method.hashCode(), parser.parseExpression(value.value(), PARSER_CONTEXT));
}
return Collections.unmodifiableMap(expressions);
}
@Nullable
@Override
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
Expression expression = expressions.get(invocation.getMethod().hashCode());
if (expression == null) {
return delegate.invoke(invocation);
}
return expression.getValue(evaluationContext, TargetWrapper.of(target, invocation.getArguments()));
}
@lombok.Value(staticConstructor = "of")
static class TargetWrapper {
Object target;
Object[] args;
}
}