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/** > /**