一般我們開發時,使用最多的還是@RequestMapping注解方式.
@RequestMapping(value = "/", param = "role=guest", consumes = "!application/json") public void myHtmlService() { // ... }
台前的是RequestMapping ,正經干活的卻是RequestCondition,根據配置的不同條件匹配request.
@RequestMapping注解,請看<SpringMVC源碼解讀 - HandlerMapping - RequestMappingHandlerMapping初始化>
典型的接口+模板.一個接口ReqeustCondition,一個抽象類,定義基礎,然后n多的具體實現.
實現中可以分為3類:基礎實現,外觀類和容器.
其中CompositeRequestCondition和RequestMappingInfo本身不帶任何的匹配條件,只是用於包裝其他的RequestCondition進行匹配
基礎實現:
consumes對應request的提交內容類型content type,如application/json, text/html
headers 對應http request 的請求頭
params對應http request parameter
Patterns對應url,就是注解value中的配置
produces指定返回的內容類型的content type,僅當request請求頭中的(Accept)類型中包含該指定類型才返回
requestMethods對應 http method,如GET,POST,PUT,DELETE等
外觀類:
RequestConditionHolder,用於不知道具體是RequestCondition哪個子類時.自定義的條件,使用的這個進行封裝
容器:
CompositeRequestCondition封裝基礎實現,具體的匹配都委托給基礎實現類.
RequestMappingInfo,對應@RequestMapping注解,一一對應注解內容與基礎實現,使用時一一委托.
先來看看RequestCondition的接口定義
1 package org.springframework.web.servlet.mvc.condition; 2 /** 3 * The contract for request conditions. 4 */ 5 public interface RequestCondition<T> { 6 7 /** 8 * 將不同的篩選條件合並 9 */ 10 T combine(T other); 11 12 /** 13 * 根據request查找匹配到的篩選條件 14 */ 15 T getMatchingCondition(HttpServletRequest request); 16 17 /** 18 * 不同篩選條件比較,用於排序 19 */ 20 int compareTo(T other, HttpServletRequest request); 21 22 } 23 }
老規矩,接下來得上抽象類AbstractRequestCondition
AbstractRequestCondition做的事不多,覆寫equals,hashCode,toString.實現equals,hashCode,toString時預留模板方法getContent();實現toString時預留模板方法getToStringInfix().
1 package org.springframework.web.servlet.mvc.condition; 2 /** 3 * A base class for {@link RequestCondition} types providing implementations of 4 * {@link #equals(Object)}, {@link #hashCode()}, and {@link #toString()}. 5 * 6 * @author Rossen Stoyanchev 7 * @since 3.1 8 */ 9 public abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> implements RequestCondition<T> { 10 11 /** 12 * Return the discrete items a request condition is composed of. 13 * For example URL patterns, HTTP request methods, param expressions, etc. 14 * @return a collection of objects, never {@code null} 15 */ 16 protected abstract Collection<?> getContent(); 17 18 @Override 19 public boolean equals(Object o) { 20 if (this == o) { 21 return true; 22 } 23 if (o != null && getClass().equals(o.getClass())) { 24 AbstractRequestCondition<?> other = (AbstractRequestCondition<?>) o; 25 return getContent().equals(other.getContent()); 26 } 27 return false; 28 } 29 30 @Override 31 public int hashCode() { 32 return getContent().hashCode(); 33 } 34 35 @Override 36 public String toString() { 37 StringBuilder builder = new StringBuilder("["); 38 for (Iterator<?> iterator = getContent().iterator(); iterator.hasNext();) { 39 Object expression = iterator.next(); 40 builder.append(expression.toString()); 41 if (iterator.hasNext()) { 42 builder.append(getToStringInfix()); 43 } 44 } 45 builder.append("]"); 46 return builder.toString(); 47 } 48 49 /** 50 * The notation to use when printing discrete items of content. 51 * For example " || " for URL patterns or " && " for param expressions. 52 */ 53 protected abstract String getToStringInfix(); 54 55 }
接下來得看具體實現了,捏不到軟柿子,用ParamsRequestCondition簡單說明下子類吧
// ParamsRequestCondition
1 // 保存解析出來的param匹配條件 2 private final Set<ParamExpression> expressions;
ParamExpression其實很簡單,看父類AbstractNameValueExpression很清楚
// AbstractNameValueExpression
1 package org.springframework.web.servlet.mvc.condition; 2 abstract class AbstractNameValueExpression<T> implements NameValueExpression<T> { 3 // 參數的名字 4 protected final String name; 5 // 參數的值 6 protected final T value; 7 // 參數的匹配規則,是= 還是!= 8 protected final boolean isNegated; 9 }
到這里我們就可以看懂,使用ParamExpression保存param參數,這樣可以任意多個.
combine的實現也就水到渠成,直接把expression拼接到一個集合里就行:
1 package org.springframework.web.servlet.mvc.condition; 2 public final class ParamsRequestCondition extends AbstractRequestCondition<ParamsRequestCondition> { 3 /** 4 * Returns a new instance with the union of the param expressions 5 * from "this" and the "other" instance. 6 */ 7 public ParamsRequestCondition combine(ParamsRequestCondition other) { 8 Set<ParamExpression> set = new LinkedHashSet<ParamExpression>(this.expressions); 9 set.addAll(other.expressions); 10 return new ParamsRequestCondition(set); 11 } 12 }
getMatchingCondition時,只要有一個不符合就判定條件不匹配
1 package org.springframework.web.servlet.mvc.condition; 2 public final class ParamsRequestCondition extends AbstractRequestCondition<ParamsRequestCondition> { 3 /** 4 * Returns "this" instance if the request matches all param expressions; 5 * or {@code null} otherwise. 6 */ 7 public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) { 8 for (ParamExpression expression : expressions) { 9 if (!expression.match(request)) { 10 return null; 11 } 12 } 13 return this; 14 } 15 }
這邊的match方法比較有意思,可以看下
1 package org.springframework.web.servlet.mvc.condition; 2 abstract class AbstractNameValueExpression<T> implements NameValueExpression<T> { 3 public final boolean match(HttpServletRequest request) { 4 boolean isMatch; 5 if (this.value != null) { 6 isMatch = matchValue(request); 7 } 8 else { // 沒有value時,只要匹配name就好 9 isMatch = matchName(request); 10 } 11 return isNegated ? !isMatch : isMatch; // 這邊需要看仔細,=與!=的處理 12 } 13 14 protected abstract boolean matchName(HttpServletRequest request); 15 16 protected abstract boolean matchValue(HttpServletRequest request); 17 }
ParamExpression中給出matchName與matchValue的實現.
ParamExpression這里又是接口+抽象實現+模板方法設計模式,偷個懶,暫時不去關心各層抽象的什么.
compareTo根據匹配條件的多少來判定順序
// ParamsRequestCondition
1 public int compareTo(ParamsRequestCondition other, HttpServletRequest request) { 2 return other.expressions.size() - this.expressions.size(); 3 }
記得還留有兩個模板方法
getContent直接返回記錄param的expressions
getToStringInfix則使用&&
// ParamsRequestCondition
1 @Override 2 protected Collection<ParamExpression> getContent() { 3 return expressions; 4 } 5 6 @Override 7 protected String getToStringInfix() { 8 return " && "; 9 }
再看看是如何解析param的
// ParamsRequestCondition
1 /** 2 * Create a new instance from the given param expressions. 3 * @param params expressions with syntax defined in {@link RequestMapping#params()}; 4 * if 0, the condition will match to every request. 5 */ 6 public ParamsRequestCondition(String... params) { 7 this(parseExpressions(params)); 8 } 9 10 private static Collection<ParamExpression> parseExpressions(String... params) { 11 Set<ParamExpression> expressions = new LinkedHashSet<ParamExpression>(); 12 if (params != null) { 13 for (String param : params) { 14 expressions.add(new ParamExpression(param)); 15 } 16 } 17 return expressions; 18 }
核心的代碼還是在AbstractNameValueExpression
// AbstractNameValueExpression
邏輯不復雜,代碼看着有點煩,是不是應該聽Martin Fowler在<重構>中的建議,來個extract method?
1 AbstractNameValueExpression(String expression) { 2 int separator = expression.indexOf('='); 3 if (separator == -1) { 4 this.isNegated = expression.startsWith("!"); 5 this.name = isNegated ? expression.substring(1) : expression; 6 this.value = null; 7 } 8 else { 9 this.isNegated = (separator > 0) && (expression.charAt(separator - 1) == '!'); 10 this.name = isNegated ? expression.substring(0, separator - 1) : expression.substring(0, separator); 11 this.value = parseValue(expression.substring(separator + 1)); 12 } 13 }
RequestCondition的解讀未完,待續:
SpringMVC源碼解讀 - RequestMapping注解實現解讀 - ConsumesRequestCondition
SpringMVC源碼解讀 - RequestMapping注解實現解讀 - RequestMappingInfo