Skip to content

Commit 935c29e

Browse files
committed
Increase scope of regex pattern cache for the SpEL matches operator
Prior to this commit, the pattern cache for the SpEL `matches` operator only applied to expressions such as the following where the same `matches` operator is invoked multiple times with different input: "map.keySet().?[#this matches '.+xyz']" The pattern cache did not apply to expressions such as the following where the same pattern ('.+xyz') is used in multiple `matches` operations: "foo matches '.+xyz' AND bar matches '.+xyz'" This commit addresses this by moving the instance of the pattern cache map from OperatorMatches to InternalSpelExpressionParser so that the cache can be reused for all `matches` operations for the given parser. Closes gh-30140
1 parent 4a3518b commit 935c29e

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -43,13 +43,28 @@ public class OperatorMatches extends Operator {
4343

4444
private static final int PATTERN_ACCESS_THRESHOLD = 1000000;
4545

46-
private final ConcurrentMap<String, Pattern> patternCache = new ConcurrentHashMap<>();
46+
private final ConcurrentMap<String, Pattern> patternCache;
4747

4848

49+
/**
50+
* Create a new {@link OperatorMatches} instance.
51+
* @deprecated as of Spring Framework 5.3.26 in favor of invoking
52+
* {@link #OperatorMatches(ConcurrentMap, int, int, SpelNodeImpl...)}
53+
* with a shared pattern cache instead
54+
*/
55+
@Deprecated(since = "5.3.26")
4956
public OperatorMatches(int startPos, int endPos, SpelNodeImpl... operands) {
50-
super("matches", startPos, endPos, operands);
57+
this(new ConcurrentHashMap<>(), startPos, endPos, operands);
5158
}
5259

60+
/**
61+
* Create a new {@link OperatorMatches} instance with a shared pattern cache.
62+
* @since 5.3.26
63+
*/
64+
public OperatorMatches(ConcurrentMap<String, Pattern> patternCache, int startPos, int endPos, SpelNodeImpl... operands) {
65+
super("matches", startPos, endPos, operands);
66+
this.patternCache = patternCache;
67+
}
5368

5469
/**
5570
* Check the first operand matches the regex specified as the second operand.

spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@
2121
import java.util.Collections;
2222
import java.util.Deque;
2323
import java.util.List;
24+
import java.util.concurrent.ConcurrentHashMap;
25+
import java.util.concurrent.ConcurrentMap;
2426
import java.util.regex.Pattern;
2527

2628
import org.springframework.expression.ParseException;
@@ -83,6 +85,7 @@
8385
* @author Andy Clement
8486
* @author Juergen Hoeller
8587
* @author Phillip Webb
88+
* @author Sam Brannen
8689
* @since 3.0
8790
*/
8891
class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
@@ -95,6 +98,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
9598
// For rules that build nodes, they are stacked here for return
9699
private final Deque<SpelNodeImpl> constructedNodes = new ArrayDeque<>();
97100

101+
// Shared cache for compiled regex patterns
102+
private final ConcurrentMap<String, Pattern> patternCache = new ConcurrentHashMap<>();
103+
98104
// The expression being parsed
99105
private String expressionString = "";
100106

@@ -248,7 +254,7 @@ private SpelNodeImpl eatRelationalExpression() {
248254
}
249255

250256
if (tk == TokenKind.MATCHES) {
251-
return new OperatorMatches(t.startPos, t.endPos, expr, rhExpr);
257+
return new OperatorMatches(this.patternCache, t.startPos, t.endPos, expr, rhExpr);
252258
}
253259

254260
Assert.isTrue(tk == TokenKind.BETWEEN, "Between token expected");

0 commit comments

Comments
 (0)