Skip to content

Commit

Permalink
Hacking
Browse files Browse the repository at this point in the history
  • Loading branch information
snicoll committed Nov 17, 2023
1 parent 7b14606 commit fd6c03e
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,13 @@
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.KotlinDetector;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
Expand Down Expand Up @@ -99,7 +101,10 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker

private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache = new ConcurrentHashMap<>(1024);

private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();
private final StandardEvaluationContext originalEvaluationContext = new StandardEvaluationContext();

private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator(
new CacheEvaluationContextFactory(this.originalEvaluationContext));

@Nullable
private final ReactiveCachingHandler reactiveCachingHandler;
Expand Down Expand Up @@ -222,6 +227,7 @@ public void setCacheManager(CacheManager cacheManager) {
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.originalEvaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}


Expand Down Expand Up @@ -852,7 +858,7 @@ protected Object generateKey(@Nullable Object result) {

private EvaluationContext createEvaluationContext(@Nullable Object result) {
return evaluator.createEvaluationContext(this.caches, this.metadata.method, this.args,
this.target, this.metadata.targetClass, this.metadata.targetMethod, result, beanFactory);
this.target, this.metadata.targetClass, this.metadata.targetMethod, result);
}

protected Collection<? extends Cache> getCaches() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2002-2023 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.cache.interceptor;

import java.lang.reflect.Method;
import java.util.function.Supplier;

import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.function.SingletonSupplier;

/**
* A factory for {@link CacheEvaluationContext} that makes sure that internal
* delegates are reused.
*
* @author Stephane Nicoll
* @since 6.1.1
*/
class CacheEvaluationContextFactory {

private final StandardEvaluationContext originalContext;

@Nullable
private Supplier<ParameterNameDiscoverer> parameterNameDiscoverer;

CacheEvaluationContextFactory(StandardEvaluationContext originalContext) {
this.originalContext = originalContext;
}

public void setParameterNameDiscoverer(Supplier<ParameterNameDiscoverer> parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}

public ParameterNameDiscoverer getParameterNameDiscoverer() {
if (this.parameterNameDiscoverer == null) {
this.parameterNameDiscoverer = SingletonSupplier.of(new DefaultParameterNameDiscoverer());
}
return this.parameterNameDiscoverer.get();
}

/**
* Creates a {@link CacheEvaluationContext} for the specified operation.
* @param rootObject the {@code root} object to use for the context
* @param targetMethod the target cache {@link Method}
* @param args the arguments of the method invocation
* @return a context suitable for this cache operation
*/
public CacheEvaluationContext forOperation(CacheExpressionRootObject rootObject,
Method targetMethod, Object[] args) {

CacheEvaluationContext evaluationContext = new CacheEvaluationContext(
rootObject, targetMethod, args, getParameterNameDiscoverer());
this.originalContext.applyDelegatesTo(evaluationContext);
return evaluationContext;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 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.
Expand All @@ -21,10 +21,8 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.cache.Cache;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
Expand Down Expand Up @@ -67,6 +65,13 @@ class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {

private final Map<ExpressionKey, Expression> unlessCache = new ConcurrentHashMap<>(64);

private final CacheEvaluationContextFactory evaluationContextFactory;

public CacheOperationExpressionEvaluator(CacheEvaluationContextFactory evaluationContextFactory) {
super();
this.evaluationContextFactory = evaluationContextFactory;
this.evaluationContextFactory.setParameterNameDiscoverer(this::getParameterNameDiscoverer);
}

/**
* Create an {@link EvaluationContext}.
Expand All @@ -81,21 +86,18 @@ class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {
*/
public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches,
Method method, Object[] args, Object target, Class<?> targetClass, Method targetMethod,
@Nullable Object result, @Nullable BeanFactory beanFactory) {
@Nullable Object result) {

CacheExpressionRootObject rootObject = new CacheExpressionRootObject(
caches, method, args, target, targetClass);
CacheEvaluationContext evaluationContext = new CacheEvaluationContext(
rootObject, targetMethod, args, getParameterNameDiscoverer());
CacheEvaluationContext evaluationContext = this.evaluationContextFactory
.forOperation(rootObject, targetMethod, args);
if (result == RESULT_UNAVAILABLE) {
evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
}
else if (result != NO_RESULT) {
evaluationContext.setVariable(RESULT_VARIABLE, result);
}
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@
import org.springframework.cache.annotation.Caching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -47,7 +50,10 @@
*/
public class ExpressionEvaluatorTests {

private final CacheOperationExpressionEvaluator eval = new CacheOperationExpressionEvaluator();
private final StandardEvaluationContext originalEvaluationContext = new StandardEvaluationContext();

private final CacheOperationExpressionEvaluator eval = new CacheOperationExpressionEvaluator(
new CacheEvaluationContextFactory(this.originalEvaluationContext));

private final AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();

Expand Down Expand Up @@ -82,7 +88,7 @@ public void testMultipleCachingEval() {
Collection<ConcurrentMapCache> caches = Collections.singleton(new ConcurrentMapCache("test"));

EvaluationContext evalCtx = this.eval.createEvaluationContext(caches, method, args,
target, target.getClass(), method, CacheOperationExpressionEvaluator.NO_RESULT, null);
target, target.getClass(), method, CacheOperationExpressionEvaluator.NO_RESULT);
Collection<CacheOperation> ops = getOps("multipleCaching");

Iterator<CacheOperation> it = ops.iterator();
Expand Down Expand Up @@ -140,14 +146,17 @@ private EvaluationContext createEvaluationContext(Object result) {
return createEvaluationContext(result, null);
}

private EvaluationContext createEvaluationContext(Object result, BeanFactory beanFactory) {
private EvaluationContext createEvaluationContext(Object result, @Nullable BeanFactory beanFactory) {
if (beanFactory != null) {
this.originalEvaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
AnnotatedClass target = new AnnotatedClass();
Method method = ReflectionUtils.findMethod(
AnnotatedClass.class, "multipleCaching", Object.class, Object.class);
Object[] args = new Object[] {new Object(), new Object()};
Collection<ConcurrentMapCache> caches = Collections.singleton(new ConcurrentMapCache("test"));
return this.eval.createEvaluationContext(
caches, method, args, target, target.getClass(), method, result, beanFactory);
caches, method, args, target, target.getClass(), method, result);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @author Stephane Nicoll
* @since 3.0
* @see SimpleEvaluationContext
* @see ReflectivePropertyAccessor
Expand Down Expand Up @@ -90,9 +91,9 @@ public class StandardEvaluationContext implements EvaluationContext {
@Nullable
private TypeConverter typeConverter;

private TypeComparator typeComparator = new StandardTypeComparator();
private TypeComparator typeComparator = StandardTypeComparator.INSTANCE;

private OperatorOverloader operatorOverloader = new StandardOperatorOverloader();
private OperatorOverloader operatorOverloader = StandardOperatorOverloader.INSTANCE;

private final Map<String, Object> variables = new ConcurrentHashMap<>();

Expand Down Expand Up @@ -329,6 +330,29 @@ public void registerMethodFilter(Class<?> type, MethodFilter filter) throws Ille
resolver.registerMethodFilter(type, filter);
}

/**
* Apply the internal delegates of this instance to the specified
* {@code evaluationContext}. Useful when a new context needs to be created per
* operation, and delegates can and should be reused. Do not modify the
* {@linkplain #setRootObject(Object) root object} or any registered
* {@linkplain #setVariables(Map) variables}.
* @param evaluationContext the evaluation context to update
* @since 6.1.1
*/
public void applyDelegatesTo(StandardEvaluationContext evaluationContext) {
// Triggers initialization for default delegates
evaluationContext.setConstructorResolvers(this.getConstructorResolvers());
evaluationContext.setMethodResolvers(this.getMethodResolvers());
evaluationContext.setPropertyAccessors(this.getPropertyAccessors());
evaluationContext.setTypeLocator(this.getTypeLocator());
evaluationContext.setTypeConverter(this.getTypeConverter());

evaluationContext.beanResolver = this.beanResolver;
evaluationContext.operatorOverloader = this.operatorOverloader;
evaluationContext.reflectiveMethodResolver = this.reflectiveMethodResolver;
evaluationContext.typeComparator = this.typeComparator;
}


private List<PropertyAccessor> initPropertyAccessors() {
List<PropertyAccessor> accessors = this.propertyAccessors;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -29,6 +29,8 @@
*/
public class StandardOperatorOverloader implements OperatorOverloader {

static final StandardOperatorOverloader INSTANCE = new StandardOperatorOverloader();

@Override
public boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
throws EvaluationException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
*/
public class StandardTypeComparator implements TypeComparator {

static final StandardTypeComparator INSTANCE = new StandardTypeComparator();

@Override
public boolean canCompare(@Nullable Object left, @Nullable Object right) {
if (left == null || right == null) {
Expand Down

0 comments on commit fd6c03e

Please sign in to comment.