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 22795a928796..6b02dbc2dbf9 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-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. @@ -36,19 +36,35 @@ * * @author Andy Clement * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ 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.2.23 in favor of invoking + * {@link #OperatorMatches(ConcurrentMap, int, int, SpelNodeImpl...)} + * with a shared pattern cache instead + */ + @Deprecated 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.2.23 + */ + 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. @@ -63,7 +79,7 @@ public BooleanTypedValue getValueInternal(ExpressionState state) throws Evaluati SpelNodeImpl leftOp = getLeftOperand(); SpelNodeImpl rightOp = getRightOperand(); String left = leftOp.getValue(state, String.class); - Object right = getRightOperand().getValue(state); + Object right = rightOp.getValue(state); if (left == null) { throw new SpelEvaluationException(leftOp.getStartPosition(), 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 5b3f69f781a4..6ca3781d0fa1 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");