SpringMvc如何將Url 映射到 RequestMapping (一)


SpringMvc Url 匹配規則詳解

最近開始閱讀Spring 源碼,雖然用了很久的spring ,但是沒有真正的分析過Spring時如何工作的。今天重 MVC 的Url匹配規則開始進行Spring源碼的閱讀。

一、Springmvc url 匹配規則

  RequestMapping中路徑的定義

  1: /abc  指定具體的定義值

  2:/{type} 指定參數 即 /###

  3:/** 匹配任何值  /###/### 可以匹配任意數量

  4:/abc/*/abc 匹配中間固定值  /abc/###/abc

二、源碼分析 

 
         
private static class PatternInfo {

            private final String pattern; // 匹配的url規則 例如 /abc/{type}

            private int uriVars; // url 參數

            private int singleWildcards;  // 單個*匹配符

            private int doubleWildcards;  // ** 匹配符

            private boolean catchAllPattern; // 是否完全匹配

            private boolean prefixPattern;  // 是否前綴匹配

            private Integer length;  // 匹配條件長度

            public PatternInfo(String pattern) {
                this.pattern = pattern;
                if (this.pattern != null) {
                    initCounters();
                    this.catchAllPattern = this.pattern.equals("/**");
                    this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**");
                }
                if (this.uriVars == 0) {
                    this.length = (this.pattern != null ? this.pattern.length() : 0);
                }
            }

            protected void initCounters() {
                int pos = 0;
                while (pos < this.pattern.length()) {
                    if (this.pattern.charAt(pos) == '{') {
                        this.uriVars++;
                        pos++;
                    }
                    else if (this.pattern.charAt(pos) == '*') {
                        if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
                            this.doubleWildcards++;
                            pos += 2;
                        }
                        else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) {
                            this.singleWildcards++;
                            pos++;
                        }
                        else {
                            pos++;
                        }
                    }
                    else {
                        pos++;
                    }
                }
            }

            public int getUriVars() {
                return this.uriVars;
            }

            public int getSingleWildcards() {
                return this.singleWildcards;
            }

            public int getDoubleWildcards() {
                return this.doubleWildcards;
            }

            public boolean isLeastSpecific() {
                return (this.pattern == null || this.catchAllPattern);
            }

            public boolean isPrefixPattern() {
                return this.prefixPattern;
            }

            public int getTotalCount() {
                return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
            }

            /**
             * Returns the length of the given pattern, where template variables are considered to be 1 long.
             */
            public int getLength() {
                if (this.length == null) {
                    this.length = VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length();
                }
                return this.length;
            }
        }
 
         
/* Pattern Url正則表達式排序規則 */
protected static class AntPatternComparator implements Comparator<String> {
      /** 將所有匹配的URL 進行排序 */
        public int compare(String pattern1, String pattern2) {
            PatternInfo info1 = new PatternInfo(pattern1);
            PatternInfo info2 = new PatternInfo(pattern2);
       
        // 規則1
if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
          // 如果 p1 和 p2 都是 /** 則兩個地址相同
return 0; } else if (info1.isLeastSpecific()) {
          // 如果 p1 是 /** 則p2優先
return 1; } else if (info2.isLeastSpecific()) {
          // 反之 p2 是 /** 則p1優先
return -1; }         // 規則2 boolean pattern1EqualsPath = pattern1.equals(path); boolean pattern2EqualsPath = pattern2.equals(path); if (pattern1EqualsPath && pattern2EqualsPath) {
        // 請求url 和p1 、p2 完全相等
return 0; } else if (pattern1EqualsPath) {
        // 請求url 和 p1 相等 則 p1 優先
return -1; } else if (pattern2EqualsPath) {
        // 請求url 和 p2 相等 則p2 優先
return 1; }         
        // 規則3
if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
          // 如果p1 是 前綴匹配 且 p2 不包含 /** 則 p2 優先
return 1; } else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
          //如果p2 是 前綴匹配 且 p1 不包含 /** 則 p1 優先
return -1; }         // 規則4 if (info1.getTotalCount() != info2.getTotalCount()) {
          // getTotalCount = this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
           // 參數匹配個數 + 單個匹配個數 + 多個匹配個數
return info1.getTotalCount() - info2.getTotalCount(); }         // 規則5 if (info1.getLength() != info2.getLength()) {
          // 正則匹配個數越多 則優先級越高
return info2.getLength() - info1.getLength(); }         // 規則6 if (info1.getSingleWildcards() < info2.getSingleWildcards()) { return -1; } else if (info2.getSingleWildcards() < info1.getSingleWildcards()) { return 1; }         // 規則7 if (info1.getUriVars() < info2.getUriVars()) { return -1; } else if (info2.getUriVars() < info1.getUriVars()) { return 1; } return 0; }

 

 

 

 

根據上面 

規則1: /** 匹配優先級最低 即當請求url其他所有都不能匹配時 則匹配 /**

規則2:路徑全匹配優先級最高

規則3:前綴匹配 和 包含/** 匹配 優先級較低

規則4:參數匹配、單個匹配、多個匹配 匹配規則數量越少 則優先級越高

規則5:正則匹配個數越多,則優先級越高 區別於4 這里是和 url進行匹配后進行比較,4中單指 規則中的匹配規則數量

規則6:單匹配 規則少則優先級高

規則7:參數匹配規則少則優先級高

 

綜合上面7個規則

總結如下:

/** 、 abc/** 規則匹配度較低 

/abc/def 匹配度最高

/{type}/def 匹配度 高於 /{type}/{name}

/{} /* /** 出現越少 匹配度越高

/{} 優先級 高於 /*

/* 優先級 高於 /**

url:/test/d_d_d/test 進行匹配 則 /test/d_d_d/test > /test/{type}/test >  /test/*/test > /test/{type}_{type}/test > /test/**/test > /test/{type}_{type}_{type}/test > /test/** > /**

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM