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