diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java index 6c9bfbaf3ed5..6c219ba03640 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -43,13 +43,28 @@ public class OperatorMatches extends Operator { private static final int PATTERN_ACCESS_THRESHOLD = 1000000; - private final ConcurrentMap patternCache = new ConcurrentHashMap<>(); + private final ConcurrentMap patternCache; + /** + * Create a new {@link OperatorMatches} instance. + * @deprecated as of Spring Framework 5.3.26 in favor of invoking + * {@link #OperatorMatches(ConcurrentMap, int, int, SpelNodeImpl...)} + * with a shared pattern cache instead + */ + @Deprecated(since = "5.3.26") public OperatorMatches(int startPos, int endPos, SpelNodeImpl... operands) { - super("matches", startPos, endPos, operands); + this(new ConcurrentHashMap<>(), startPos, endPos, operands); } + /** + * Create a new {@link OperatorMatches} instance with a shared pattern cache. + * @since 5.3.26 + */ + public OperatorMatches(ConcurrentMap patternCache, int startPos, int endPos, SpelNodeImpl... operands) { + super("matches", startPos, endPos, operands); + this.patternCache = patternCache; + } /** * Check the first operand matches the regex specified as the second operand. diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 33c4d9db4eb3..748f8e6bd524 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 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. @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; import org.springframework.expression.ParseException; @@ -83,6 +85,7 @@ * @author Andy Clement * @author Juergen Hoeller * @author Phillip Webb + * @author Sam Brannen * @since 3.0 */ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { @@ -95,6 +98,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // For rules that build nodes, they are stacked here for return private final Deque constructedNodes = new ArrayDeque<>(); + // Shared cache for compiled regex patterns + private final ConcurrentMap patternCache = new ConcurrentHashMap<>(); + // The expression being parsed private String expressionString = ""; @@ -248,7 +254,7 @@ private SpelNodeImpl eatRelationalExpression() { } if (tk == TokenKind.MATCHES) { - return new OperatorMatches(t.startPos, t.endPos, expr, rhExpr); + return new OperatorMatches(this.patternCache, t.startPos, t.endPos, expr, rhExpr); } Assert.isTrue(tk == TokenKind.BETWEEN, "Between token expected");