Re-use EvaluationContext in DefaultSubscriptionRegistry

Rather than create a new EvaluationContext instance per evaluation, we
now create a statically shared instance, without the root object in it,
and re-use it for all evalutations.
This commit is contained in:
Rossen Stoyanchev 2018-03-21 23:29:58 -04:00
parent 19293b9847
commit e0de9126ed
1 changed files with 29 additions and 22 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,7 +26,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser; import org.springframework.expression.ExpressionParser;
@ -34,7 +33,7 @@ import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
@ -65,6 +64,11 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
/** Default maximum number of entries for the destination cache: 1024 */ /** Default maximum number of entries for the destination cache: 1024 */
public static final int DEFAULT_CACHE_LIMIT = 1024; public static final int DEFAULT_CACHE_LIMIT = 1024;
/** Static evaluation context to re-use */
private static SimpleEvaluationContext evaluationContext = SimpleEvaluationContext.builder()
.propertyAccessor(new SimpMessageHeaderPropertyAccessor()).build();
private PathMatcher pathMatcher = new AntPathMatcher(); private PathMatcher pathMatcher = new AntPathMatcher();
@ -192,7 +196,6 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
if (!this.selectorHeaderInUse) { if (!this.selectorHeaderInUse) {
return allMatches; return allMatches;
} }
EvaluationContext context = null;
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(allMatches.size()); MultiValueMap<String, String> result = new LinkedMultiValueMap<>(allMatches.size());
for (String sessionId : allMatches.keySet()) { for (String sessionId : allMatches.keySet()) {
for (String subId : allMatches.get(sessionId)) { for (String subId : allMatches.get(sessionId)) {
@ -209,12 +212,8 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
result.add(sessionId, subId); result.add(sessionId, subId);
continue; continue;
} }
if (context == null) {
context = new StandardEvaluationContext(message);
context.getPropertyAccessors().add(new SimpMessageHeaderPropertyAccessor());
}
try { try {
if (Boolean.TRUE.equals(expression.getValue(context, Boolean.class))) { if (Boolean.TRUE.equals(expression.getValue(evaluationContext, message, Boolean.class))) {
result.add(sessionId, subId); result.add(sessionId, subId);
} }
} }
@ -533,7 +532,7 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
@Override @Override
public Class<?>[] getSpecificTargetClasses() { public Class<?>[] getSpecificTargetClasses() {
return new Class<?>[] {MessageHeaders.class}; return new Class<?>[] {Message.class, MessageHeaders.class};
} }
@Override @Override
@ -542,21 +541,29 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
} }
@Override @Override
public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException { public TypedValue read(EvaluationContext context, @Nullable Object target, String name) {
Assert.state(target instanceof MessageHeaders, "No MessageHeaders");
MessageHeaders headers = (MessageHeaders) target;
SimpMessageHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(headers, SimpMessageHeaderAccessor.class);
Assert.state(accessor != null, "No SimpMessageHeaderAccessor");
Object value; Object value;
if ("destination".equalsIgnoreCase(name)) { if (target instanceof Message) {
value = accessor.getDestination(); value = name.equals("headers") ? ((Message) target).getHeaders() : null;
}
else if (target instanceof MessageHeaders) {
MessageHeaders headers = (MessageHeaders) target;
SimpMessageHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(headers, SimpMessageHeaderAccessor.class);
Assert.state(accessor != null, "No SimpMessageHeaderAccessor");
if ("destination".equalsIgnoreCase(name)) {
value = accessor.getDestination();
}
else {
value = accessor.getFirstNativeHeader(name);
if (value == null) {
value = headers.get(name);
}
}
} }
else { else {
value = accessor.getFirstNativeHeader(name); // Should never happen...
if (value == null) { throw new IllegalStateException("Expected Message or MessageHeaders.");
value = headers.get(name);
}
} }
return new TypedValue(value); return new TypedValue(value);
} }