SpringMVC源码解读 - RequestMapping注解实现解读 - ConsumesRequestCondition
consumes 指定处理请求的提交内容类型(media-Type),例如application/json, text/html.
所以这边的ConsumesRequestCondition就是通过content type进行url过滤的.
具体分析前,我们先准备下基础知识.
1. MediaType(org.springframework.http包下,封装n多api)
2. MediaTypeExpression接口,对MediaType和是否匹配关系(=或!=)的封装
3. AbstractMediaTypeExpression体系,也就是ConsumeMediaTypeExpression和ProduceMediaTypeExpression.
// MediaTypeExpression接口
1 package org.springframework.web.servlet.mvc.condition; 2 public interface MediaTypeExpression { 3 4 MediaType getMediaType(); 5 6 boolean isNegated(); 7 8 }
AbstractMediaTypeExpression构造方法处理是否!后,委托MediaType处理
// AbstractMediaTypeExpression
1 AbstractMediaTypeExpression(String expression) { 2 if (expression.startsWith("!")) { 3 isNegated = true; 4 expression = expression.substring(1); 5 } 6 else { 7 isNegated = false; 8 } 9 this.mediaType = MediaType.parseMediaType(expression); 10 }
看看条件匹配的match
// AbstractMediaTypeExpression
1 public final boolean match(HttpServletRequest request) { 2 try { 3 boolean match = matchMediaType(request); 4 return !isNegated ? match : !match; 5 } 6 catch (HttpMediaTypeException ex) { 7 return false; 8 } 9 } 10 11 protected abstract boolean matchMediaType(HttpServletRequest request) throws HttpMediaTypeException;
Consume和Produce的区别终于出来了
1 static class ConsumeMediaTypeExpression extends AbstractMediaTypeExpression { 2 @Override 3 protected boolean matchMediaType(HttpServletRequest request) throws HttpMediaTypeNotSupportedException { 4 try { 5 MediaType contentType = StringUtils.hasLength(request.getContentType()) ? 6 MediaType.parseMediaType(request.getContentType()) : 7 MediaType.APPLICATION_OCTET_STREAM; 8 return getMediaType().includes(contentType); 9 } 10 catch (IllegalArgumentException ex) { 11 throw new HttpMediaTypeNotSupportedException( 12 "Can't parse Content-Type [" + request.getContentType() + "]: " + ex.getMessage()); 13 } 14 } 15 }
1 private final List<ProduceMediaTypeExpression> MEDIA_TYPE_ALL_LIST = 2 Collections.singletonList(new ProduceMediaTypeExpression("*/*")); 3 4 5 /** 6 * Parses and matches a single media type expression to a request's 'Accept' header. 7 */ 8 class ProduceMediaTypeExpression extends AbstractMediaTypeExpression { 9 10 @Override 11 protected boolean matchMediaType(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException { 12 List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(request); 13 for (MediaType acceptedMediaType : acceptedMediaTypes) { 14 if (getMediaType().isCompatibleWith(acceptedMediaType)) { 15 return true; 16 } 17 } 18 return false; 19 } 20 }
正文
combine方法,这边不做合并,直接覆盖
// ConsumesRequestCondition
1 /** 2 * Returns the "other" instance if it has any expressions; returns "this" 3 * instance otherwise. Practically that means a method-level "consumes" 4 * overrides a type-level "consumes" condition. 5 */ 6 public ConsumesRequestCondition combine(ConsumesRequestCondition other) { 7 return !other.expressions.isEmpty() ? other : this; 8 }
getMatchingCondition的规则是存在一个即判定匹配
// ConsumesRequestCondition
1 /** 2 * Checks if any of the contained media type expressions match the given 3 * request 'Content-Type' header and returns an instance that is guaranteed 4 * to contain matching expressions only. The match is performed via 5 * {@link MediaType#includes(MediaType)}. 6 * 7 * @param request the current request 8 * 9 * @return the same instance if the condition contains no expressions; 10 * or a new condition with matching expressions only; 11 * or {@code null} if no expressions match. 12 */ 13 public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) { 14 if (isEmpty()) { 15 return this; 16 } 17 Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<ConsumeMediaTypeExpression>(expressions); 18 for (Iterator<ConsumeMediaTypeExpression> iterator = result.iterator(); iterator.hasNext();) { 19 ConsumeMediaTypeExpression expression = iterator.next(); 20 if (!expression.match(request)) { 21 iterator.remove(); 22 } 23 } 24 return (result.isEmpty()) ? null : new ConsumesRequestCondition(result); 25 }
compareTo谁有匹配规则谁牛逼,都有的话比较第一个expression
// ConsumesRequestCondition
1 public int compareTo(ConsumesRequestCondition other, HttpServletRequest request) { 2 if (this.expressions.isEmpty() && other.expressions.isEmpty()) { 3 return 0; 4 } 5 else if (this.expressions.isEmpty()) { 6 return 1; 7 } 8 else if (other.expressions.isEmpty()) { 9 return -1; 10 } 11 else { 12 return this.expressions.get(0).compareTo(other.expressions.get(0)); 13 } 14 }
看看初始化吧
1 /** 2 * Creates a new instance with "consumes" and "header" expressions. 3 * "Header" expressions where the header name is not 'Content-Type' or have 4 * no header value defined are ignored. If 0 expressions are provided in 5 * total, the condition will match to every request 6 * @param consumes as described in {@link RequestMapping#consumes()} 7 * @param headers as described in {@link RequestMapping#headers()} 8 */ 9 public ConsumesRequestCondition(String[] consumes, String[] headers) { 10 this(parseExpressions(consumes, headers)); 11 } 12 13 private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, String[] headers) { 14 Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<ConsumeMediaTypeExpression>(); 15 if (headers != null) { 16 for (String header : headers) { 17 HeaderExpression expr = new HeaderExpression(header); 18 if ("Content-Type".equalsIgnoreCase(expr.name)) { 19 for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) { 20 result.add(new ConsumeMediaTypeExpression(mediaType, expr.isNegated)); 21 } 22 } 23 } 24 } 25 if (consumes != null) { 26 for (String consume : consumes) { 27 result.add(new ConsumeMediaTypeExpression(consume)); 28 } 29 } 30 return result; 31 }