SpringMVC源碼解讀 - RequestMapping注解實現解讀 - RequestCondition體系


一般我們開發時,使用最多的還是@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

 


免責聲明!

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



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