SpringMvc源码学习_第一节RequestMappingInfo与@RequestMaping

    public @interface RequestMapping {
     
    	String[] value() default {};
    	RequestMethod[] method() default {};
    	String[] params() default {};
    	String[] headers() default {};
    	String[] consumes() default {};
    	String[] produces() default {};
     
    }


@RequestMapping 注解想必大家都不陌生。

springMVC在启动时会扫描所有的@RequestMapping并封装成对应的RequestMapingInfo。

一个请求过来会与RequestMapingInfo进行逐个比较,找到最适合那个RequestMapingInfo。

@RequestMapping 的属性基本上都是String数组类型,在转成RequestMappingInfo对象时,同样会转成其他类型。

 

RequestMappingHandlerMapping.java  可以看到转换的过程:

        /**
	 * Created a RequestMappingInfo from a RequestMapping annotation.
	 */
	protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
		String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
		return new RequestMappingInfo(
				new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
						this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
				new RequestMethodsRequestCondition(annotation.method()),
				new ParamsRequestCondition(annotation.params()),
				new HeadersRequestCondition(annotation.headers()),
				new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
				new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()),
				customCondition);
	}

现在我们来看一下。RequestMappingInfo、PattrensRequestCondition、RequestMethodsRequestCondition……。

通过上图可以看出他们都实现了RequestCondition。

典型的接口+模板.一个接口ReqeustCondition,一个抽象类,定义基础,然后n多的具体实现.
实现中可以分为3类:基础实现,外观类 和 容器

1、基础实现:
  1、consumes对应request的提交内容类型content type,如application/json, text/html

  2、headers 对应http request 的请求头

  3、params  对应http request parameter

  4、Patterns对应url,就是注解value中的配置

  5、produces指定返回的内容类型的content type,仅当request请求头中的(Accept)类型中包含该指定类型才返回

  6、requestMethods对应 http method,如GET,POST,PUT,DELETE等


2、外观类:

  RequestConditionHolder,用于不知道具体是RequestCondition哪个子类时.自定义的条件,使用的这个进行封装


3、容器:
  CompositeRequestCondition和RequestMappingInfo本身不带任何的匹配条件,只是用于包装其他的RequestCondition进行匹配

  CompositeRequestCondition封装基础实现,具体的匹配都委托给基础实现类.

       RequestMappingInfo,对应@RequestMapping注解,一一对应注解内容与基础实现,.

 

原码分析:

RequestCondition:

public interface RequestCondition<T> {
 
	/**
	 * Defines the rules for combining this condition (i.e. the current instance)
	 * with another condition. For example combining type- and method-level
	 * {@link RequestMapping} conditions.
	 * @param other the condition to combine with.
	 * @return a request condition instance that is the result of combining
	 * the two condition instances.
	 *
	 * 将不同的筛选条件合并
	 */
	T combine(T other);
 
	/**
	 * Checks if this condition matches the given request and returns a
	 * potentially new request condition with content tailored to the
	 * current request. For example a condition with URL patterns might
	 * return a new condition that contains matching patterns sorted
	 * with best matching patterns on top.
	 * @return a condition instance in case of a match;
	 * or {@code null} if there is no match
	 *
	 * 根据request查找匹配到的筛选条件
	 */
	T getMatchingCondition(HttpServletRequest request);
 
	/**
	 * Compares this condition to another condition in the context of
	 * a specific request. This method assumes both instances have
	 * been obtained via {@link #getMatchingCondition(HttpServletRequest)}
	 * to ensure they have content relevant to current request only.
	 *
	 * 不同筛选条件比较,用于排序
	 */
	int compareTo(T other, HttpServletRequest request);
 
}

老规矩,接下来得上抽象类AbstractRequestCondition

 

AbstractRequestCondition做的事不多,覆写equals,hashCode,toString.实现equals,hashCode,toString时预留模板方法getContent();

实现toString时预留模板方法getToStringInfix().

    /**
     * A base class for {@link RequestCondition} types providing implementations of
     * {@link #equals(Object)}, {@link #hashCode()}, and {@link #toString()}.
     *
     * @author Rossen Stoyanchev
     * @since 3.1
     */
    public abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> implements RequestCondition<T> {
     
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj) {
    			return true;
    		}
    		if (obj != null && getClass().equals(obj.getClass())) {
    			AbstractRequestCondition<?> other = (AbstractRequestCondition<?>) obj;
    			return getContent().equals(other.getContent());
    		}
    		return false;
    	}
     
    	@Override
    	public int hashCode() {
    		return getContent().hashCode();
    	}
     
    	@Override
    	public String toString() {
    		StringBuilder builder = new StringBuilder("[");
    		for (Iterator<?> iterator = getContent().iterator(); iterator.hasNext();) {
    			Object expression = iterator.next();
    			builder.append(expression.toString());
    			if (iterator.hasNext()) {
    				builder.append(getToStringInfix());
    			}
    		}
    		builder.append("]");
    		return builder.toString();
    	}
     
     
    	/**
    	 * Return the discrete items a request condition is composed of.
    	 * For example URL patterns, HTTP request methods, param expressions, etc.
    	 * @return a collection of objects, never {@code null}
    	 */
    	protected abstract Collection<?> getContent();
     
    	/**
    	 * The notation to use when printing discrete items of content.
    	 * For example " || " for URL patterns or " && " for param expressions.
    	 */
    	protected abstract String getToStringInfix();
     
    }

接下来再看一下RequestMappingInfo这个类。

这是一个集合容器类,包含类有7个condition。6个对应@RequestMaping中的属性,1个是用于不知道具体是RequestCondition哪个子类时.自定义的条件,使用的这个进行封装

/**
 * Encapsulates the following request mapping conditions:
 * <ol>
 * 	<li>{@link PatternsRequestCondition}
 * 	<li>{@link RequestMethodsRequestCondition}
 * 	<li>{@link ParamsRequestCondition}
 * 	<li>{@link HeadersRequestCondition}
 * 	<li>{@link ConsumesRequestCondition}
 * 	<li>{@link ProducesRequestCondition}
 * 	<li>{@code RequestCondition} (optional, custom request condition)
 * </ol>
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @since 3.1
 */
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
 
	private final PatternsRequestCondition patternsCondition;
 
	private final RequestMethodsRequestCondition methodsCondition;
 
	private final ParamsRequestCondition paramsCondition;
 
	private final HeadersRequestCondition headersCondition;
 
	private final ConsumesRequestCondition consumesCondition;
 
	private final ProducesRequestCondition producesCondition;
 
	private final RequestConditionHolder customConditionHolder;
 
 
	/**
	 * Creates a new instance with the given request conditions.
	 */
	public RequestMappingInfo(PatternsRequestCondition patterns, RequestMethodsRequestCondition methods,
			ParamsRequestCondition params, HeadersRequestCondition headers, ConsumesRequestCondition consumes,
			ProducesRequestCondition produces, RequestCondition<?> custom) {
 
		this.patternsCondition = (patterns != null ? patterns : new PatternsRequestCondition());
		this.methodsCondition = (methods != null ? methods : new RequestMethodsRequestCondition());
		this.paramsCondition = (params != null ? params : new ParamsRequestCondition());
		this.headersCondition = (headers != null ? headers : new HeadersRequestCondition());
		this.consumesCondition = (consumes != null ? consumes : new ConsumesRequestCondition());
		this.producesCondition = (produces != null ? produces : new ProducesRequestCondition());
		this.customConditionHolder = new RequestConditionHolder(custom);
	}
 
	/**
	 * Re-create a RequestMappingInfo with the given custom request condition.
	 */
	public RequestMappingInfo(RequestMappingInfo info, RequestCondition<?> customRequestCondition) {
		this(info.patternsCondition, info.methodsCondition, info.paramsCondition, info.headersCondition,
				info.consumesCondition, info.producesCondition, customRequestCondition);
	}
 
 
	/**
	 * Returns the URL patterns of this {@link RequestMappingInfo};
	 * or instance with 0 patterns, never {@code null}.
	 */
	public PatternsRequestCondition getPatternsCondition() {
		return this.patternsCondition;
	}
 
	/**
	 * Returns the HTTP request methods of this {@link RequestMappingInfo};
	 * or instance with 0 request methods, never {@code null}.
	 */
	public RequestMethodsRequestCondition getMethodsCondition() {
		return this.methodsCondition;
	}
 
	/**
	 * Returns the "parameters" condition of this {@link RequestMappingInfo};
	 * or instance with 0 parameter expressions, never {@code null}.
	 */
	public ParamsRequestCondition getParamsCondition() {
		return this.paramsCondition;
	}
 
	/**
	 * Returns the "headers" condition of this {@link RequestMappingInfo};
	 * or instance with 0 header expressions, never {@code null}.
	 */
	public HeadersRequestCondition getHeadersCondition() {
		return this.headersCondition;
	}
 
	/**
	 * Returns the "consumes" condition of this {@link RequestMappingInfo};
	 * or instance with 0 consumes expressions, never {@code null}.
	 */
	public ConsumesRequestCondition getConsumesCondition() {
		return this.consumesCondition;
	}
 
	/**
	 * Returns the "produces" condition of this {@link RequestMappingInfo};
	 * or instance with 0 produces expressions, never {@code null}.
	 */
	public ProducesRequestCondition getProducesCondition() {
		return this.producesCondition;
	}
 
	/**
	 * Returns the "custom" condition of this {@link RequestMappingInfo}; or {@code null}.
	 */
	public RequestCondition<?> getCustomCondition() {
		return this.customConditionHolder.getCondition();
	}
 
 
	/**
	 * Combines "this" request mapping info (i.e. the current instance) with another request mapping info instance.
	 * <p>Example: combine type- and method-level request mappings.
	 * @return a new request mapping info instance; never {@code null}
	 */
	public RequestMappingInfo combine(RequestMappingInfo other) {
		PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
		RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
		ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
		HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
		ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
		ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
		RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);
 
		return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom.getCondition());
	}
 
	/**
	 * Checks if all conditions in this request mapping info match the provided request and returns
	 * a potentially new request mapping info with conditions tailored to the current request.
	 * <p>For example the returned instance may contain the subset of URL patterns that match to
	 * the current request, sorted with best matching patterns on top.
	 * @return a new instance in case all conditions match; or {@code null} otherwise
	 */
	public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
		RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
		ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
		HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
		ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
		ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
 
		if (methods == null || params == null || headers == null || consumes == null || produces == null) {
			return null;
		}
 
		PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
		if (patterns == null) {
			return null;
		}
 
		RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
		if (custom == null) {
			return null;
		}
 
		return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom.getCondition());
	}
 
 
	/**
	 * Compares "this" info (i.e. the current instance) with another info in the context of a request.
	 * <p>Note: It is assumed both instances have been obtained via
	 * {@link #getMatchingCondition(HttpServletRequest)} to ensure they have conditions with
	 * content relevant to current request.
	 */
	public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
		int result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.producesCondition.compareTo(other.getProducesCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
		if (result != 0) {
			return result;
		}
		result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
		if (result != 0) {
			return result;
		}
		return 0;
	}
 
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj != null && obj instanceof RequestMappingInfo) {
			RequestMappingInfo other = (RequestMappingInfo) obj;
			return (this.patternsCondition.equals(other.patternsCondition) &&
					this.methodsCondition.equals(other.methodsCondition) &&
					this.paramsCondition.equals(other.paramsCondition) &&
					this.headersCondition.equals(other.headersCondition) &&
					this.consumesCondition.equals(other.consumesCondition) &&
					this.producesCondition.equals(other.producesCondition) &&
					this.customConditionHolder.equals(other.customConditionHolder));
		}
		return false;
	}
 
	@Override
	public int hashCode() {
		return (this.patternsCondition.hashCode() * 31 +  // primary differentiation
				this.methodsCondition.hashCode() + this.paramsCondition.hashCode() +
				this.headersCondition.hashCode() + this.consumesCondition.hashCode() +
				this.producesCondition.hashCode() + this.customConditionHolder.hashCode());
	}
 
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder("{");
		builder.append(this.patternsCondition);
		builder.append(",methods=").append(this.methodsCondition);
		builder.append(",params=").append(this.paramsCondition);
		builder.append(",headers=").append(this.headersCondition);
		builder.append(",consumes=").append(this.consumesCondition);
		builder.append(",produces=").append(this.producesCondition);
		builder.append(",custom=").append(this.customConditionHolder);
		builder.append('}');
		return builder.toString();
	}
 
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值