diff --git a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java index 1021a8fceb..5108f47482 100644 --- a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -50,13 +50,13 @@ import java.util.regex.Pattern; */ public class AntPathMatcher implements PathMatcher { + /** Default path separator: "/" */ + public static final String DEFAULT_PATH_SEPARATOR = "/"; + private static final int CACHE_TURNOFF_THRESHOLD = 65536; private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}"); - /** Default path separator: "/" */ - public static final String DEFAULT_PATH_SEPARATOR = "/"; - private String pathSeparator = DEFAULT_PATH_SEPARATOR; @@ -64,8 +64,9 @@ public class AntPathMatcher implements PathMatcher { private volatile Boolean cachePatterns; - final Map stringMatcherCache = - new ConcurrentHashMap(256); + private final Map tokenizedPatternCache = new ConcurrentHashMap(256); + + final Map stringMatcherCache = new ConcurrentHashMap(256); /** @@ -81,7 +82,7 @@ public class AntPathMatcher implements PathMatcher { * Default is {@code true}. */ public void setTrimTokens(boolean trimTokens) { - this.trimTokens = trimTokens; + this.trimTokens = trimTokens; } /** @@ -99,6 +100,12 @@ public class AntPathMatcher implements PathMatcher { this.cachePatterns = cachePatterns; } + private void deactivatePatternCache() { + this.cachePatterns = false; + this.tokenizedPatternCache.clear(); + this.stringMatcherCache.clear(); + } + @Override public boolean isPattern(String path) { @@ -128,8 +135,8 @@ public class AntPathMatcher implements PathMatcher { return false; } - String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true); - String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); + String[] pattDirs = tokenizePattern(pattern); + String[] pathDirs = tokenizePath(path); int pattIdxStart = 0; int pattIdxEnd = pattDirs.length - 1; @@ -248,6 +255,44 @@ public class AntPathMatcher implements PathMatcher { return true; } + /** + * Tokenize the given path pattern into parts, based on this matcher's settings. + *

Performs caching based on {@link #setCachePatterns}, delegating to + * {@link #tokenizePath(String)} for the actual tokenization algorithm. + * @param pattern the pattern to tokenize + * @return the tokenized pattern parts + */ + protected String[] tokenizePattern(String pattern) { + String[] tokenized = null; + Boolean cachePatterns = this.cachePatterns; + if (cachePatterns == null || cachePatterns.booleanValue()) { + tokenized = this.tokenizedPatternCache.get(pattern); + } + if (tokenized == null) { + tokenized = tokenizePath(pattern); + if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) { + // Try to adapt to the runtime situation that we're encountering: + // There are obviously too many different patterns coming in here... + // So let's turn off the cache since the patterns are unlikely to be reoccurring. + deactivatePatternCache(); + return tokenized; + } + if (cachePatterns == null || cachePatterns.booleanValue()) { + this.tokenizedPatternCache.put(pattern, tokenized); + } + } + return tokenized; + } + + /** + * Tokenize the given path String into parts, based on this matcher's settings. + * @param path the path to tokenize + * @return the tokenized path parts + */ + protected String[] tokenizePath(String path) { + return StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); + } + /** * Tests whether or not a string matches against a pattern. * @param pattern the pattern to match against (never {@code null}) @@ -281,10 +326,9 @@ public class AntPathMatcher implements PathMatcher { matcher = new AntPathStringMatcher(pattern); if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) { // Try to adapt to the runtime situation that we're encountering: - // There are obviously too many different paths coming in here... + // There are obviously too many different patterns coming in here... // So let's turn off the cache since the patterns are unlikely to be reoccurring. - this.cachePatterns = false; - this.stringMatcherCache.clear(); + deactivatePatternCache(); return matcher; } if (cachePatterns == null || cachePatterns.booleanValue()) {