AntPathMatcher 1

Spring工具类AntPathMatcher [1]

  • AntPathMatcher的继承层次关系
    • PathMatcher接口方法定义
    • AntPathMatcher实现接口方法细节
      • isPattern
      • match

AntPathMatcher的继承层次关系

这个类实现PathMatcher这个接口.

PathMatcher接口方法定义

  1. boolean isPattern(string path): 用来检查给定的**path(pattern–匹配规则)**是否是个合法的匹配规则.
  2. boolean match(string pattern, string path):依据pattern的匹配策略来匹配path是否符合规则.
  3. boolean matchStart(String pattern, String path):确定该模式是否至少与给定的基本路径匹配
  4. String extractPathWithinPattern(String pattern, String path):剥离匹配模式的前导静态部分,只返回动态部分匹配的path.

    例如:pattern = "myroot/*.html" ; path = "myroot/myfile.html" 会返回 myfile.html

  5. Map extractUriTemplateVariables(String pattern, String path):匹配URI,并返回URI参数map;

    例如:pattern = "/hotels/{hotel}" path = "/hotels/1"那么返回的map包含hotel->1

  6. Comparator getPatternComparator(String path):根据完整路径,返回一个适配匹配模式匹配路径排序的比较器
  7. String combine(String pattern1, String pattern2): 整合两个模式,合并成1个新模式并返回

AntPathMatcher实现接口方法细节

isPattern

@Override
    public boolean isPattern(String path) {
        return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
    }

包含*或者?就代表这个path是个匹配模式

match

首先看一下这个方法的调用时序图

AntPathMatcher 1_第1张图片

默认分隔符是/,首先 path 如果以分隔符开始 那么pattern必须也以分隔符开始,否则返回false

if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
            return false;
        }

使用分隔符把pattern分隔成string数组,观察下面代码使用了ConcurrentHashMap作为缓存分隔好的模式提升性能.

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);
            //当缓存大于65536这个阈值的时候讲不再缓存,并且清理缓存
            if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
                deactivatePatternCache();
                return tokenized;
            }
            if (cachePatterns == null || cachePatterns.booleanValue()) {
                this.tokenizedPatternCache.put(pattern, tokenized);
            }
        }
        return tokenized;
    }

如果是全路径并且区分大小写,那么就检查path是否有潜在匹配的可能,如果没有,返回不匹配,反之亦然

    if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) {
            return false;

        }

isPotentialMatch 源代码

    private boolean isPotentialMatch(String path, String[] pattDirs) {
    //如果去除标记的空格设置true (默认false)直接返回成功
        if (!this.trimTokens) {
            int pos = 0;
            for (String pattDir : pattDirs) {
            //跳过分隔符并返回位置
                int skipped = skipSeparator(path, pos, this.pathSeparator);
                pos += skipped;
                //检查模式中是否存在通配符('*', '?', '{'),存在跳出,不存在且字符匹配skipped+1,并返回skipped
                skipped = skipSegment(path, pos, pattDir);
                //如果小于模式的长度,意味着不全等,也可能是遇到通配符跳出了.. 所以只有skipped=0 并且没有通配符.才返回false
                if (skipped < pattDir.length()) {
                    return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0))));
                }
                pos += skipped;
            }
        }
        return true;
    }

如果上面匹配了,接着使用分隔符把需要匹配的path分隔成string数组,这里面使用了java.util 里面的StringTokenizer来处理字符串字符.

public static String[] tokenizeToStringArray(
            @Nullable String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {

        if (str == null) {
            return new String[0];
        }

        StringTokenizer st = new StringTokenizer(str, delimiters);
        List<String> tokens = new ArrayList<>();
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            if (trimTokens) {
                token = token.trim();
            }
            if (!ignoreEmptyTokens || token.length() > 0) {
                tokens.add(token);
            }
        }
        return toStringArray(tokens);
    }

接着就是对这两个字符数组从左到右开始匹配…如果模式字符匹配到**那么就意味着首部匹配通过.否则讲ant规则字符转换成对应的正则模式,然后用java的正则匹配.
?表示正则的.,看下面代码

        // up to last '**'
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            String pattDir = pattDirs[pattIdxStart];
            if ("**".equals(pattDir)) {
                break;
            }
          //转换规则
            if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
                return false;
            }
            pattIdxStart++;
            pathIdxStart++;
        }
        //此时如果需要匹配的字符串全匹配了
            if (pathIdxStart > pathIdxEnd) {
        
            if (pattIdxStart > pattIdxEnd) {
                return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
            }
            //如果不需要全匹配 就直接返回true
            if (!fullMatch) {
                return true;
            }
                // path 全匹配了..如果还存在模式的话 只能是* 和 ** 了
            if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
                return true;
            }
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (!pattDirs[i].equals("**")) {
                    return false;
                }
            }
            return true;
        }else if (pattIdxStart > pattIdxEnd) {
            // 模式数组匹配完了,path没有匹配完的情况.失败
            return false;
        }    else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
            // 不需要全匹配的情况 .匹配到 ** 就是true
            return true;
        }
        //然后从模式尾部开始一轮匹配(因为前面可能遇到**跳出来了,所以需要从尾部继续匹配)
        `while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd)`
        `if (pathIdxStart > pathIdxEnd)`
        //要是尾部匹配还是遇到**break跳出了 ,只要模式前后不等,并且还有需要匹配的字符进入这个循环,否则直接判断模式接下来的字符只要不等于**就是不匹配
        while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
           //定义一个临时模式的起始位置
            int patIdxTmp = -1;
            //从pattIdxStart+1开始检查,因为前面的已经再上面检查过了.
            for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
                //i次时遇到**,临时模式位置变为i
                if (pattDirs[i].equals("**")) {
                    patIdxTmp = i;
                    break;
                }
            }
            if (patIdxTmp == pattIdxStart + 1) {
                // '**/**' 出现这种情况的时候跳过1个
                pattIdxStart++;
                continue;
            }
            // 去中间的字符,和临时模式和结尾模式中间的模式字符
            // strIdxStart & strIdxEnd
            int patLength = (patIdxTmp - pattIdxStart - 1);
            int strLength = (pathIdxEnd - pathIdxStart + 1);
            int foundIdx = -1;
            //循环 剩余字符长度 减去 剩余模式长度 次数 取字符.
            strLoop:
            for (int i = 0; i <= strLength - patLength; i++) {
              //很明显匹配到**才有可能进入下面的循环
                for (int j = 0; j < patLength; j++) {
                    String subPat = pattDirs[pattIdxStart + j + 1];
                    String subStr = pathDirs[pathIdxStart + i + j];
                    if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
                        continue strLoop;
                    }
                }
                //匹配到了 
                foundIdx = pathIdxStart + i;
                break;
            }

            if (foundIdx == -1) {
                return false;
            }
            //把临时模式起始位置赋值给pattIdxStart
            pattIdxStart = patIdxTmp;
              //foundIdx(匹配到的字符位置) + patLength(剩余模式长度)赋值给pathIdxStart
            pathIdxStart = foundIdx + patLength;
        }
        // patIdxTmp后面还有模式的话  都必须的是**
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
            if (!pattDirs[i].equals("**")) {
                return false;
            }
        }
        ----------------------------------------------------
            public AntPathStringMatcher(String pattern, boolean caseSensitive) {
            StringBuilder patternBuilder = new StringBuilder();
            Matcher matcher = GLOB_PATTERN.matcher(pattern);
            int end = 0;
            while (matcher.find()) {
                patternBuilder.append(quote(pattern, end, matcher.start()));
                String match = matcher.group();
                if ("?".equals(match)) {
                    patternBuilder.append('.');
                }
                else if ("*".equals(match)) {
                    patternBuilder.append(".*");
                }
                else if (match.startsWith("{") && match.endsWith("}")) {
                    int colonIdx = match.indexOf(':');
                    if (colonIdx == -1) {
                        patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
                        this.variableNames.add(matcher.group(1));
                    }
                    else {
                        String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
                        patternBuilder.append('(');
                        patternBuilder.append(variablePattern);
                        patternBuilder.append(')');
                        String variableName = match.substring(1, colonIdx);
                        this.variableNames.add(variableName);
                    }
                }
                end = matcher.end();
            }
            patternBuilder.append(quote(pattern, end, pattern.length()));
            this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
                    Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
        }

未完待续…

你可能感兴趣的:(java,开发语言,spring)