{
/**
* Combine this condition with another such as conditions from a
* type-level and method-level {@code @RequestMapping} annotation.
* @param other the condition to combine with.
* @return a request condition instance that is the result of combining
* the two condition instances.
* 翻译下就是: 将当前controller类上的@RequestMapping对应的RequestMappingInfo和方法上的@RequestMapping对应的RequestMappingInfo对象进行合 并
*/
T combine(T other);
/**
* Check if the condition matches the request returning a potentially new
* instance created for the current request. For example a condition with
* multiple URL patterns may return a new instance only with those patterns
* that match the request.
* For CORS pre-flight requests, conditions should match to the would-be,
* actual request (e.g. URL pattern, query parameters, and the HTTP method
* from the "Access-Control-Request-Method" header). If a condition cannot
* be matched to a pre-flight request it should return an instance with
* empty content thus not causing a failure to match.
* @return a condition instance in case of a match or {@code null} otherwise.
* 翻译下: 获取与请求匹配的条件实例, 即与当前条件匹配的结果。举例: 一个方法上使用的@RequestMapping注解上配置的method属性为(method = {RequestMethod.GET, RequestMethod.POST}), 那么注册到MappingRegistry中的RequestMappingInfo中的方法是包含GET和POST的, 但是我实际的request请求如果是GET, 那么匹配封装之后的新的RequestMappingInfo中的method就只包含GET, 没有POST!!!
*/
@Nullable
T getMatchingCondition(HttpServletRequest request);
/**
* Compare 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.
* 当我们一个请求匹配多个RequestMappingInfo时, 就需要对这些RequestMappingInfo进行排序, 判断哪个跟请求是最匹配的。
*/
int compareTo(T other, HttpServletRequest request);
}
```
RequestCondition的几个核心方法见上。接下来我们分析RequestMappingInfo对于上述接口的具体实现。以及在何处使用?
1.首先看RequestMappingInfo#combine方法
```java
public RequestMappingInfo combine(RequestMappingInfo other) {
String name = combineNames(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(name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
```
可以看到,该方法的实现是针对对其属性的合并。它的属性xxxRequestCondition,也都实现了RequestCondition接口,所以也是实现了combine方法。那么combine方法是在什么场景下使用的呢?答案其实在step4中已经介绍了。在初始化Bean -> RequestMappingHandlerMapping 时,调用其afterPropertiesSet方法,在该方法中会扫描含@RequestMapping或@Controller注解的class,并解析,将其注册到MappingRegistry中,其中就包含了url -> Collection<\RequestMappingInfo>的,map,这里就涉及到RequestMappingInfo的创建了。这里会先后创建class上@RequestMapping注解对应的RequestMappingInfo以及方法上@RequestMapping注解对应的RequestMappingInfo对象,并将二者合并!关于它其中的属性是如何合并的,我们后面再分析。
2.再看RequestMappingInfo#getMatchingCondition方法
```java
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (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(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition());
}
```
可以看到,该方法的实现,本质上也是调用其各个属性的getMatchingCondition方法。这个方法在何时调用的,又有什么作用呢?在step2和step3中,如何根据请求获取到对应的处理器方法中,首先通过url从MappingRegistry中查询对应的Collection<\RequestMappingInfo>,这里Collection中的RequestMappingInfo是上面combine方法调用之后的RequestMappingInfo。再获取到该Collection之后,会遍历当前Collection,取出每一个RequestMappingInfo对象,调用其getMatchingCondition方法,获取一个新的匹配的RequestMappingInfo!假设我们的合并之后的@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}), 那么注册到MappingRegistry中的RequestMappingInfo中的方法是包含GET和POST的, 但是我实际的request请求如果是GET, 那么匹配封装之后的新的RequestMappingInfo中的method就只包含GET, 没有POST!!!
3.最后看RequestMappingInfo#compareTo方法
```java
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result;
// Automatic vs explicit HTTP HEAD mapping
// 先忽略
if (HttpMethod.HEAD.matches(request.getMethod())) {
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
}
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;
}
// Implicit (no method) vs explicit HTTP method mappings
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;
}
```
可以看到,RequestMappingInfo的优先级比较也是其属性的优先级比较。比较的先后顺序是:PatternsRequestCondition> ParamsRequestCondition > HeadersRequestCondition > ConsumesRequestCondition > ProducesRequestCondition > RequestMethodsRequestCondition > customConditionHolder(自定义的,默认为null)。那么这个方法在何时调用呢?在step2中,当我们通过请求和getMatchingCondition方法获取到多个RequestMappingInfo时,就需要找到最佳匹配的RequestMappingInfo。如果排序后的前两个匹配的compareTo方法返回0,说明匹配度相同,那么直接抛异常!
接下来,我们介绍下属性中复杂度最高的PatternsRequestCondition(请求路径条件)
step6 PatternsRequestCondition
先看它的几个核心属性:
```java
public class PatternsRequestCondition extends AbstractRequestCondition {
// 路径模式集合, 可以为空, 为空时匹配所有请求路径
private final Set patterns;
// 路径工具类, 用于获取请求的请求路径, 实现类为: UrlPathHelper
private final UrlPathHelper pathHelper;
// 路径匹配器, 用于执行路径匹配逻辑, 实现类为: AntPathMatcher(很重要!)
private final PathMatcher pathMatcher;
// 是否启用匹配后缀(.*), 默认为false
private final boolean useSuffixPatternMatch;
// 是否启用匹配不上时添加/后缀尝试匹配, 默认为true
private final boolean useTrailingSlashMatch;
// 已知的文件扩展名列表, 默认为空, 不执行文件扩展匹配
private final List fileExtensions = new ArrayList<>();
}
```
前面已经介绍过,当前当前class也实现了RequestCondition接口,而且RequestMappingInfo的三个核心方法的实现,也就是其各个xxxRequestCondition属性的三个核心方法的调用,所以我们接下来再分析PatternsRequestCondition是如何实现这三个方法的即可。
PatternsRequestCondition#combine
```java
// 合并
public PatternsRequestCondition combine(PatternsRequestCondition other) {
if (isEmptyPathPattern() && other.isEmptyPathPattern()) {
return this;
}
else if (other.isEmptyPathPattern()) {
return this;
}
else if (isEmptyPathPattern()) {
return other;
}
Set result = new LinkedHashSet<>();
if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
for (String pattern1 : this.patterns) {
for (String pattern2 : other.patterns) {
result.add(this.pathMatcher.combine(pattern1, pattern2));
}
}
}
return new PatternsRequestCondition(result, this);
}
```
PatternsRequestCondition#getMatchingCondition
```java
public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
// 从请求中获取查找的路径
String lookupPath = this.pathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
// 这个方法下面介绍
List matches = getMatchingPatterns(lookupPath);
return !matches.isEmpty() ? new PatternsRequestCondition(new LinkedHashSet<>(matches), this) : null;
}
// 路径匹配并排序
public List getMatchingPatterns(String lookupPath) {
List matches = null;
for (String pattern : this.patterns) {
String match = getMatchingPattern(pattern, lookupPath);
if (match != null) {
matches = (matches != null ? matches : new ArrayList<>());
matches.add(match);
}
}
if (matches == null) {
return Collections.emptyList();
}
if (matches.size() > 1) {
matches.sort(this.pathMatcher.getPatternComparator(lookupPath));
}
return matches;
}
```
PatternsRequestCondition#compareTo
```java
public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
String lookupPath = this.pathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
Comparator patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
Iterator iterator = this.patterns.iterator();
Iterator iteratorOther = other.patterns.iterator();
while (iterator.hasNext() && iteratorOther.hasNext()) {
int result = patternComparator.compare(iterator.next(), iteratorOther.next());
if (result != 0) {
return result;
}
}
if (iterator.hasNext()) {
return -1;
}
else if (iteratorOther.hasNext()) {
return 1;
}
else {
return 0;
}
}
```
综上,我们已经分析完了如何根据请求获取HandlerMethod的整个过程。这里遗留一个问题:如何将HandlerMethod封装成HandlerExecutionChain。接下来简单讲解下。
step7 HandlerExecutionChain
我们知道,根据请求获取到处理器方法HandlerMethod之后,会将HandlerMethod和HandlerInterceptor进行封装成对象HandlerExecutionChain。那么,有哪些HandlerInterceptor呢?如何封装呢?
首先,在springboot中,RequestMappingHandlerMapping的@Bean构造器中,会创建添加两个HandlerInterceptor。如下:
```java
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
// 这里创建并添加了HandlerInterceptor
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer configurer = getPathMatchConfigurer();
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
mapping.setUrlPathHelper(pathHelper);
}
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
mapping.setPathMatcher(pathMatcher);
}
Map>> pathPrefixes = configurer.getPathPrefixes();
if (pathPrefixes != null) {
mapping.setPathPrefixes(pathPrefixes);
}
return mapping;
}
```
其次,RequestMappingHandlerMapping的祖先类ApplicationObjectSupport,实现了ApplicationContextAware接口,在spring容器初始化RequestMappingHandlerMapping对象的过程中,会在第七次使用BeanPostProcessor时回调ApplicationContextAware接口的setApplicationContext方法,如下:
```java
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
// Reset internal context state.
this.applicationContext = null;
this.messageSourceAccessor = null;
}
else if (this.applicationContext == null) {
// Initialize with passed-in context.
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
}
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
// 这里是重点!!!
initApplicationContext(context);
}
else {
// Ignore reinitialization if same context passed in.
if (this.applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this.applicationContext + "], passed-in one is [" + context + "]");
}
}
}
```
在这个方法中,会调用initApplicationContext(ApplicationContext context)方法,这个方法最终会调用子类的AbstractHandlerMapping的initApplicationContext方法。接下来分析下AbstractHandlerMapping#initApplicationContext方法
AbstractHandlerMapping#initApplicationContext
```java
protected void initApplicationContext() throws BeansException {
// 调用这个方法之前this.interceptors中已经包含了2个HandlerInterceptor
// 这里的extendInterceptors, 默认是空实现
extendInterceptors(this.interceptors);
// 检测并添加MappedInterceptor, 默认也为空
detectMappedInterceptors(this.adaptedInterceptors);
// 添加到adaptedInterceptors列表中
initInterceptors();
}
```
当前interceptor都存储在AbstractHandlerMapping父类中,如下:
```java
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered, BeanNameAware {
@Nullable
private Object defaultHandler;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
private final List