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

时间: 2024-08-03 23:27:24

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系的相关文章

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo

使用@RequestMapping注解时,配置的信息最后都设置到了RequestMappingInfo中. RequestMappingInfo封装了PatternsRequestCondition,RequestMethodsRequestCondition,ParamsRequestCondition等,所以自己不干活,所有的活都是委托给具体的condition处理. 先看下封装的RequestCondition吧,之前的文章将的比较细了,不清楚各个类具体是做什么的,可以移步这里<Sprin

SpringMVC源码解读 - HandlerMapping

SpringMVC在请求到handler处理器的分发这步是通过HandlerMapping模块解决的.handlerMapping 还处理拦截器. 先看看HandlerMapping的继承树吧 可以大致这样做个分类: 1. 一个接口HandlerMapping,定义一个api: HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; 2. 一个基础抽象类:主要是准备上下文环境,提供getHand

7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解

从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()) 这样的方式来执行request的handler方法. 先来分析一下ha.handle方法的调用过程:HandlerAdapter接口有一个抽象实现类AbstractHandlerMethodAdapter,在该抽象类中通过具体方法

SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解

转自 SpringMVC关于json.xml自动转换的原理研究[附带源码分析] 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Spring源码解析 https://blog.csdn.net/column/details/21851.html 部分代码会放在我的的Github:https://github.com/h2pl/ 目录 前言 现象 源码分析 实例讲解 关于配置 总结 参考资料 前言 SpringMVC是目前主流的Web MVC

8、SpringMVC源码分析(3):分析ModelAndView的形成过程

首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception方法开始,看看这个牛逼的ModelAndView是怎么开始的,又是怎么结束的: 1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Except

springmvc源码浅析(基于spring3.1.0)

请求处理过程:通过url找到对应Controller类中处理请求的方法,执行方法返回结果视图的过程.大致分为三个步骤: 其一,ApplicationContext初始化时建立所有url和controller类的对应关系(用Map保存); 其二,根据请求url找到对应的controller,并从controller中找到处理请求的方法; 其三,执行方法处理请求,并返回结果视图. 我们首先看第一个步骤,也就是建立Map<url,controller>关系的部分.第一部分的入口类为Applicati

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping

接<SpringMVC源码分析(5)剖析重要组件HandlerMapping>,继续剖析HandlerMapping,DefaultAnnotationHandlerMapping是SpringMVC 中最重要的HandlerMapping组件.虽然它在spring3.1版本后被废弃了. 包括2部分内容 DefaultAnnotationHandlerMapping剖析 HandlerMapping的拦截器 1.DefaultAnnotationHandlerMapping剖析 鉴于它的重要地

SpringMVC源码剖析(五)-消息转换器HttpMessageConverter

SpringMVC源码剖析(五)-消息转换器HttpMessageConverter 目录[-] 概述 Http请求的抽象 HttpInputMessage HttpOutputMessage HttpMessageConverter RequestResponseBodyMethodProcessor 思考 概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Sprin

SpringMVC源码 —— 消息转换器HttpMessageConverter

SpringMVC使用消息转换器实现请求报文和对象.对象和响应报文之间的自动转换 概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制. Http请求的抽象 还是回到请求-响应,也就是解析请求体,然后返回响应报文这个最基本的Http请求过程中来.我们知道,在servlet标准中,可以用ja