code-learning/springmvc/12-Spring MVC 源码解析-HandlerMapping 组件(三)之 AbstractHandlerMethodMapping.md

1780 lines
70 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 Spring MVC 源码解析 —— HandlerMapping 组件(三)之 AbstractHandlerMethodMapping
# 1. 概述
本文接 [《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(一)之 AbstractHandlerMapping》](http://svip.iocoder.cn/Spring-MVC/HandlerMapping-1-AbstractHandlerMapping) 一文,分享 AbstractHandlerMapping 的右半边 **AbstractHandlerMethodMapping** 系,即下图右半边**黄色**部分:
[![类图](12-Spring MVC 源码解析-HandlerMapping 组件(三)之 AbstractHandlerMethodMapping.assets/01.png)](http://static.iocoder.cn/images/Spring/2022-03-01/01.png)类图
- 一共有三个子类继承关系很简单AbstractHandlerMethodMapping <= RequestMappingInfoHandlerMapping <= RequestMappingHandlerMapping 。所以,我们在下面的每一小节,分享一个类。
# 2. 注解
Spring MVC 的请求匹配的注解,我们已经是熟悉的不能再熟悉,如下:
- [`org.springframework.web.bind.annotation.@RequestMapping`](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java)
- [`org.springframework.web.bind.annotation.@GetMapping`](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/bind/annotation/GetMapping.java)
- [`org.springframework.web.bind.annotation.@PostMapping`](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/bind/annotation/PostMapping.java)
- [`org.springframework.web.bind.annotation.@PutMapping`](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/bind/annotation/PutMapping.java)
- [`org.springframework.web.bind.annotation.@DeleteMapping`](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/bind/annotation/DeleteMapping.java)
- [`org.springframework.web.bind.annotation.@PatchMapping`](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/bind/annotation/PatchMapping.java)
具体每个注解有哪些属性,胖友点击链接瞅瞅即可。毕竟,太熟悉了。哈哈哈哈。
# 3. AbstractHandlerMethodMapping
`org.springframework.web.servlet.result.method.AbstractHandlerMethodMapping` ,实现 InitializingBean 接口,继承 AbstractHandlerMapping 抽象类,以 **Method** 作为 **Handler** 的 HandlerMapping 抽象类,提供 Mapping 的初始化、注册等通用的骨架方法。这就是我们常说的[“模板方法模式”](http://www.iocoder.cn/DesignPattern/xiaomingge/Template-Method/) 。
那么具体是什么呢AbstractHandlerMethodMapping 定义为了 `<T>` 泛型,交给子类做决定。例如,子类 RequestMappingInfoHandlerMapping 使用 RequestMappingInfo 类作为 `<T>` 泛型,也就是我们在 [「2. 注解」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 看到的 `@RequestMapping` 等注解。
## 3.1 构造方法
```
// AbstractHandlerMethodMapping.java
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* Mapping 注册表
*/
private final MappingRegistry mappingRegistry = new MappingRegistry();
/**
* Mapping 命名策略
*/
@Nullable
private HandlerMethodMappingNamingStrategy<T> namingStrategy;
}
```
- `<T>` 泛型就是我们上面提到的Mapping 的类型。
- `mappingRegistry` 属性Mapping 注册表。详细解析,见 [「3.2 MappingRegistry」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `namingStrategy` 属性,`org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy` 接口,😈 名字好长Handler **的** Method **的** Mapping **的**名字生成策略接口。代码如下:
```
// HandlerMethodMappingNamingStrategy.java
@FunctionalInterface
public interface HandlerMethodMappingNamingStrategy<T> {
/**
* 获得名字
*
* Determine the name for the given HandlerMethod and mapping.
* @param handlerMethod the handler method
* @param mapping the mapping
* @return the name
*/
String getName(HandlerMethod handlerMethod, T mapping);
}
```
- 可能不太好理解,获得 Mapping 的名字。这样,我们就可以根据 Mapping 的名字,获得 Handler 。
- `org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMethodMappingNamingStrategy` 实现该接口,代码如下:
```
// RequestMappingInfoHandlerMethodMappingNamingStrategy.java
public class RequestMappingInfoHandlerMethodMappingNamingStrategy
implements HandlerMethodMappingNamingStrategy<RequestMappingInfo> {
/** Separator between the type and method-level parts of a HandlerMethod mapping name. */
public static final String SEPARATOR = "#";
@Override
public String getName(HandlerMethod handlerMethod, RequestMappingInfo mapping) {
// 情况一mapping 名字非空,则使用 mapping 的名字
if (mapping.getName() != null) {
return mapping.getName();
}
// 情况二,使用类名大写 + "#" + 方法名
StringBuilder sb = new StringBuilder();
String simpleTypeName = handlerMethod.getBeanType().getSimpleName();
for (int i = 0 ; i < simpleTypeName.length(); i++) {
if (Character.isUpperCase(simpleTypeName.charAt(i))) {
sb.append(simpleTypeName.charAt(i));
}
}
sb.append(SEPARATOR).append(handlerMethod.getMethod().getName());
return sb.toString();
}
}
```
- 比较好理解,分成两种情况。
- 情况一,如果 Mapping 已经配置名字,则直接返回。例如,`@RequestMapping(name = "login", value = "user/login")` 注解的方法,它对应的 Mapping 的名字就是 `"login"` 。
- 情况二,如果 Mapping 未配置名字,则使用使用类名大写 + `"#"` + 方法名。例如,`@RequestMapping(value = "user/login")` 注解的方法,假设它所在的类为 UserController ,对应的方法名为 login ,则它对应的 Mapping 的名字就是 `USERCONTROLLER#login` 。
## 3.2 MappingRegistry
MappingRegistry ,是 AbstractHandlerMethodMapping 的私有类Mapping 注册表。
### 3.2.1 构造方法
```
// AbstractHandlerMethodMapping.java#MappingRegistry
/**
* 注册表
*
* KEY: Mapping
*/
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
/**
* 注册表2
*
* KEYMapping
*/
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
/**
* 直接 URL 的映射
*
* KEY直接 URL
* VALUEMapping 数组
*/
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
/**
* Mapping 的名字与 HandlerMethod 的映射
*
* KEYMapping 的名字
* VALUEHandlerMethod 数组
*/
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
/**
* TODO 1012 cors
*/
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
/**
* 读写锁
*/
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
```
- `registry` 属性,注册表 1 。其中KEY 为 Mapping ,即 `<T>` 泛型。
- `mappingLookup` 属性,注册表 2 。其中KEY 为 Mapping ,即 `<T>` 泛型。
- `urlLookup` 属性,直接 URL 的映射。其中KEY 为直接 URL VALUE 为 Mapping 数组。比较不好理解,我们放在 [「3.2.2 register」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 一起看。
- `nameLookup` 属性Mapping 的名字与 HandlerMethod 的映射。其中KEY 为 Mapping 的名字( 就是 [「3.1 构造方法」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 中,我们看到的 HandlerMethodMappingNamingStrategy 生成的 Mapping 的名字 )VALUE 为 HandlerMethod 数组。
- `readWriteLock` 属性,读写锁。下文,会看到它的使用。涉及它的有两个方法:
```
// AbstractHandlerMethodMapping.java#MappingRegistry
/**
* 获得读锁
*
* Acquire the read lock when using getMappings and getMappingsByUrl.
*/
public void acquireReadLock() {
this.readWriteLock.readLock().lock();
}
/**
* 获得写锁
*
* Release the read lock after using getMappings and getMappingsByUrl.
*/
public void releaseReadLock() {
this.readWriteLock.readLock().unlock();
}
```
### 3.2.2 register
`#register(T mapping, Object handler, Method method)` 方法,注册。代码如下:
```
// AbstractHandlerMethodMapping.java#MappingRegistry
public void register(T mapping, Object handler, Method method) {
// <1> 获得写锁
this.readWriteLock.writeLock().lock();
try {
// <2.1> 创建 HandlerMethod 对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// <2.2> 校验当前 mapping 不存在,否则抛出 IllegalStateException 异常
assertUniqueMethodMapping(handlerMethod, mapping);
// <2.3> 添加 mapping + HandlerMethod 到 mappingLookup 中
this.mappingLookup.put(mapping, handlerMethod);
// <3.1> 获得 mapping 对应的普通 URL 数组
List<String> directUrls = getDirectUrls(mapping);
// <3.2> 添加到 url + mapping 到 urlLookup 集合中
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
// <4> 初始化 nameLookup
String name = null;
if (getNamingStrategy() != null) {
// <4.1> 获得 Mapping 的名字
name = getNamingStrategy().getName(handlerMethod, mapping);
// <4.2> 添加到 mapping 的名字 + HandlerMethod 到 nameLookup 中
addMappingName(name, handlerMethod);
}
// <5> TODO 1012 cors
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// <6> 创建 MappingRegistration 对象,并 mapping + MappingRegistration 添加到 registry 中
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod));
} finally {
// <7> 释放写锁
this.readWriteLock.writeLock().unlock();
}
}
```
- `<1>` 处,获得写锁。
- `<2.1>` 处,调用 `#createHandlerMethod(Object handler, Method method)` 方法,创建 HandlerMethod 对象。详细解析,见 [「3.3 createHandlerMethod」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<2.2>` 处,调用 `#assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping)` 方法,校验当前 `mapping` 不存在,否则抛出 IllegalStateException 异常。代码如下:
```
// AbstractHandlerMethodMapping.java#MappingRegistry
private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) {
HandlerMethod handlerMethod = this.mappingLookup.get(mapping);
if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) { // 存在,且不相等,说明不唯一
throw new IllegalStateException(
"Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" +
newHandlerMethod + "\nto " + mapping + ": There is already '" +
handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
}
}
```
- `<2.3>` 处,添加 `mapping` + HandlerMethod 到 `mappingLookup` 中。
- `<3.1>` 处,调用 `#getDirectUrls(T mapping)` 方法,获得 mapping 对应的直接 URL 数组。代码如下:
```
// AbstractHandlerMethodMapping.java#MappingRegistry
private List<String> getDirectUrls(T mapping) {
List<String> urls = new ArrayList<>(1);
// 遍历 Mapping 对应的路径
for (String path : getMappingPathPatterns(mapping)) {
// 非**模式**路径
if (!getPathMatcher().isPattern(path)) {
urls.add(path);
}
}
return urls;
}
```
- 例如,`@RequestMapping("/user/login")` 注解对应的路径,**就是**直接路径。
- 例如,`@RequestMapping("/user/${id}")` 注解对应的路径,**不是**直接路径。
- `<3.2>` 处,添加到 直接 url + `mapping` 到 `urlLookup` 集合中。
- `<4.1>` 处,调用 `HandlerMethodMappingNamingStrategy#getName(HandlerMethod handlerMethod, T mapping)` 方法,获得 Mapping 的名字。
- `<4.2>` 处,调用 `#addMappingName(String name, HandlerMethod handlerMethod)` 方法,添加 `mapping` 的名字 + HandlerMethod 到 `nameLookup` 中。代码如下:
```
// AbstractHandlerMethodMapping.java#MappingRegistry
private void addMappingName(String name, HandlerMethod handlerMethod) {
// 获得 Mapping 的名字,对应的 HandlerMethod 数组
List<HandlerMethod> oldList = this.nameLookup.get(name);
if (oldList == null) {
oldList = Collections.emptyList();
}
// 如果已经存在,则不用添加
for (HandlerMethod current : oldList) {
if (handlerMethod.equals(current)) {
return;
}
}
// 添加到 nameLookup 中
List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1); // 重新创建的原因是,保证数组的大小固定。因为,基本不太存在扩容的可能性,申请大了就浪费了。
newList.addAll(oldList);
newList.add(handlerMethod);
this.nameLookup.put(name, newList);
}
```
- 比较简单,胖友自己瞅瞅就懂了。
- `<5>` 处TODO 1012 cors
- `<6>` 处,创建 MappingRegistration 对象,并 `mapping` + MappingRegistration 添加到 `registry` 中。关于 MappingRegistration 类,详细解析,见 [「3.4 MappingRegistration」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<7>` 处,释放写锁。
### 3.2.3 unregister
`#unregister(T mapping)` 方法,取消注册。代码如下:
```
// AbstractHandlerMethodMapping.java#MappingRegistry
public void unregister(T mapping) {
// 获得写锁
this.readWriteLock.writeLock().lock();
try {
// 从 registry 中移除
MappingRegistration<T> definition = this.registry.remove(mapping);
if (definition == null) {
return;
}
// 从 mappingLookup 中移除
this.mappingLookup.remove(definition.getMapping());
// 从 urlLookup 移除
for (String url : definition.getDirectUrls()) {
List<T> list = this.urlLookup.get(url);
if (list != null) {
list.remove(definition.getMapping());
if (list.isEmpty()) {
this.urlLookup.remove(url);
}
}
}
// 从 nameLookup 移除
removeMappingName(definition);
// 从 corsLookup 中移除
this.corsLookup.remove(definition.getHandlerMethod());
} finally {
// 释放写锁
this.readWriteLock.writeLock().unlock();
}
}
```
- 比较简单,胖友自己瞅瞅。另外,`#removeMappingName(MappingRegistration<T> definition)` 方法,胖友也自己瞅瞅。
## 3.3 createHandlerMethod
`#createHandlerMethod(Object handler, Method method)` 方法,创建 HandlerMethod 对象。代码如下:
```
// AbstractHandlerMethodMapping.java
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
HandlerMethod handlerMethod;
// <1> 如果 handler 类型为 String 说明对应一个 Bean 对象,例如 UserController 使用 @Controller 注解后,默认 handler 为它的 beanName ,即 `userController`
if (handler instanceof String) {
String beanName = (String) handler;
handlerMethod = new HandlerMethod(beanName,
obtainApplicationContext().getAutowireCapableBeanFactory(), method);
// <2> 如果 handler 类型非 String ,说明是一个已经是一个 handler 对象,就无需处理,直接创建 HandlerMethod 对象
} else {
handlerMethod = new HandlerMethod(handler, method);
}
return handlerMethod;
}
```
- `<1>` 处,如果 `handler` 类型**为** String 说明对应一个 Bean 对象,例如 UserController 使用 `@Controller` 注解后, `handler` 为它的默认的 `beanName = userController` 。
- `<2>` 处,如果 `handler` 类型**非** String ,说明是一个已经是一个 `handler` 对象,就无需处理,直接创建 HandlerMethod 对象。
- 到了此处我们已经可以觉察实际上HandlerMethod 是 `handler` + `method` 的组合,一个对象的某个方法。
### 3.3.1 HandlerMethod
`org.springframework.web.method.HandlerMethod` ,处理器的方法的封装对象。属性如下:
```
// HandlerMethod.java
/**
* Bean 对象
*/
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
/**
* Bean 的类型
*/
private final Class<?> beanType;
/**
* 方法
*/
private final Method method;
/**
* {@link #method} 的桥接方法
*
* 详细说明
*
* 1. https://www.jianshu.com/p/250030ea9b28
* 2. https://blog.csdn.net/mhmyqn/article/details/47342577
*/
private final Method bridgedMethod;
/**
* 方法参数数组
*/
private final MethodParameter[] parameters;
/**
* 响应的状态码,即 {@link ResponseStatus#code()}
*/
@Nullable
private HttpStatus responseStatus;
/**
* 响应的状态码原因,即 {@link ResponseStatus#reason()}
*/
@Nullable
private String responseStatusReason;
/**
* 解析自哪个 HandlerMethod 对象
*
* 仅构造方法中传入 HandlerMethod 类型的参数适用,例如 {@link #HandlerMethod(HandlerMethod)}
*/
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
/**
* 父接口的方法的参数注解数组
*/
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
```
- 属性,虽然比较多,但是比较好理解。我们顺着构造方法,一起来看看。
**HandlerMethod(String beanName, BeanFactory beanFactory, Method method) 构造方法**
对应 `#createHandlerMethod(Object handler, Method method)` 方法的 `<1>` 的情况,代码如下:
```
// HandlerMethod.java
/**
* Create an instance from a bean name, a method, and a {@code BeanFactory}.
* The method {@link #createWithResolvedBean()} may be used later to
* re-create the {@code HandlerMethod} with an initialized bean.
*/
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
Assert.hasText(beanName, "Bean name is required");
Assert.notNull(beanFactory, "BeanFactory is required");
Assert.notNull(method, "Method is required");
// <1> 将 beanName 赋值给 bean 属性,说明 beanFactory + bean 的方式,获得 handler 对象
this.bean = beanName;
this.beanFactory = beanFactory;
// <2> 初始化 beanType 属性
Class<?> beanType = beanFactory.getType(beanName);
if (beanType == null) {
throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
}
this.beanType = ClassUtils.getUserClass(beanType);
// <3> 初始化 method、bridgedMethod 属性
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
// <4> 初始化 parameters 属性
this.parameters = initMethodParameters();
// <5> 初始化 responseStatus、responseStatusReason 属性
evaluateResponseStatus();
}
```
- `<1>` 处, 将 `beanName` 赋值给 `bean` 属性,说明 `beanFactory` + `bean` 的方式,获得 `handler` 对象。
- `<2>` 处,初始化 `beanType` 属性。
- `<3>` 处,初始化 `method`、`bridgedMethod` 属性。
- `<5>` 处,调用 `#evaluateResponseStatus()` 方法,初始化 `responseStatus`、`responseStatusReason` 属性。代码如下:
```
// HandlerMethod.java
private void evaluateResponseStatus() {
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
if (annotation == null) {
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
this.responseStatus = annotation.code();
this.responseStatusReason = annotation.reason();
}
}
```
- `<4>` 处,调用 `#initMethodParameters()` 方法,初始化 `parameters` 属性。代码如下:
```
// HandlerMethod.java
private MethodParameter[] initMethodParameters() {
int count = this.bridgedMethod.getParameterCount();
// 创建 MethodParameter 数组
MethodParameter[] result = new MethodParameter[count];
// 遍历 bridgedMethod 的参数,逐个解析参数类型
for (int i = 0; i < count; i++) {
HandlerMethodParameter parameter = new HandlerMethodParameter(i);
GenericTypeResolver.resolveParameterType(parameter, this.beanType);
result[i] = parameter;
}
return result;
}
```
- 关于 MethodParameter 和 GenericTypeResolver 类,本文就不详细解析,胖友自己去瞅瞅。
- 关于 HandlerMethodParameter TODO 1014 HandlerMethodParameter
------
**HandlerMethod(Object bean, Method method) 构造方法**
对应 `#createHandlerMethod(Object handler, Method method)` 方法的 `<2>` 的情况,代码如下:
```
// HandlerMethod.java
/**
* Create an instance from a bean instance and a method.
*/
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "Bean is required");
Assert.notNull(method, "Method is required");
// 初始化 bean
this.bean = bean;
this.beanFactory = null; // 置空 beanFactory ,因为不用
// 初始化 beanType 属性
this.beanType = ClassUtils.getUserClass(bean);
// 初始化 method 和 bridgedMethod 属性
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
// 初始化 parameters 属性
this.parameters = initMethodParameters();
// 初始化 responseStatus、responseStatusReason 属性
evaluateResponseStatus();
}
```
- 灰常简单。
## 3.4 MappingRegistration
MappingRegistration ,是 AbstractHandlerMethodMapping 的私有静态类Mapping 注册登记。代码如下:
```
// AbstractHandlerMethodMapping.java
private static class MappingRegistration<T> {
/**
* Mapping 对象
*/
private final T mapping;
/**
* HandlerMethod 对象
*/
private final HandlerMethod handlerMethod;
/**
* 直接 URL 数组
*/
private final List<String> directUrls;
/**
* {@link #mapping} 的名字
*/
@Nullable
private final String mappingName;
public MappingRegistration(T mapping, HandlerMethod handlerMethod,
@Nullable List<String> directUrls, @Nullable String mappingName) {
Assert.notNull(mapping, "Mapping must not be null");
Assert.notNull(handlerMethod, "HandlerMethod must not be null");
this.mapping = mapping;
this.handlerMethod = handlerMethod;
this.directUrls = (directUrls != null ? directUrls : Collections.emptyList());
this.mappingName = mappingName;
}
// ... 省略 getting 方法
}
```
## 3.5 afterPropertiesSet
> 艿艿:打起精神,虽然前面已经看了很多代码。此处,我们才真正进行**初始化**。
**实现** `#afterPropertiesSet()` 方法,进行初始化。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* 是否只扫描可访问的 HandlerMethod 们
*/
private boolean detectHandlerMethodsInAncestorContexts = false;
@Override
public void afterPropertiesSet() {
// <x> 初始化处理器的方法们
initHandlerMethods();
}
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
// <1.1> 遍历 Bean ,逐个处理
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// <1.2> 处理 Bean
processCandidateBean(beanName);
}
}
// <2> 初始化处理器的方法们。目前是空方法,暂无具体的实现
handlerMethodsInitialized(getHandlerMethods());
}
```
- `<1.1>` 处,调用 `#getCandidateBeanNames()` 方法,获得所有 Bean 的名字们,然后进行遍历处理。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Determine the names of candidate beans in the application context.
* @since 5.1
* @see #setDetectHandlerMethodsInAncestorContexts
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors
*/
protected String[] getCandidateBeanNames() {
return (this.detectHandlerMethodsInAncestorContexts ? // 可访问
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}
```
- `<1.2>` 处,调用 `#processCandidateBean(String beanName)` 方法,处理 Bean。详细解析见 [「3.5.1 processCandidateBean」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<2>` 处,调用 `#handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods)` 方法,初始化处理器的方法们。目前是空方法,暂无具体的实现。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Invoked after all handler methods have been detected.
* @param handlerMethods a read-only map with handler methods and mappings.
*/
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
}
```
- 所以,可以忽略这个方法。
### 3.5.1 processCandidateBean
`#processCandidateBean(String beanName)` 方法,判断 Bean 是否为处理器,如果是,则扫描处理器方法。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Determine the type of the specified candidate bean and call
* {@link #detectHandlerMethods} if identified as a handler type.
* <p>This implementation avoids bean creation through checking
* {@link org.springframework.beans.factory.BeanFactory#getType}
* and calling {@link #detectHandlerMethods} with the bean name.
* @param beanName the name of the candidate bean
* @since 5.1
* @see #isHandler
* @see #detectHandlerMethods
*/
protected void processCandidateBean(String beanName) {
// <1> 获得 Bean 对应的类型
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
} catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 判断 Bean 是否为处理器,如果是,则扫描处理器方法
if (beanType != null && isHandler(beanType)) { // <2.1>
detectHandlerMethods(beanName); // <2.2>
}
}
```
- `<1>` 处,获得 Bean 对应的类型。
- `<2.1>` 处,调用 `#isHandler(Class<?> beanType)` **抽象**方法,判断 Bean 的类型是否为处理器。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Whether the given type is a handler with handler methods.
* @param beanType the type of the bean being checked
* @return "true" if this a handler type, "false" otherwise.
*/
protected abstract boolean isHandler(Class<?> beanType);
```
- 该方法在 RequestMappingHandlerMapping 类中实现。详细解析,见 [「5.3 isHandler」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<2.2>` 处,调用 `#detectHandlerMethods(Object handler)` 方法, 扫描处理器的方法们。详细解析,见 [「3.5.2 detectHandlerMethods」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
### 3.5.2 detectHandlerMethods
`#handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods)` 方法,初始化处理器的方法们。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Look for handler methods in a handler.
* @param handler the bean name of a handler or a handler instance
*/
protected void detectHandlerMethods(final Object handler) {
// <1> 获得处理器类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// <2> 获得真实的类。因为handlerType 可能是代理类
final Class<?> userType = ClassUtils.getUserClass(handlerType);
// <3> 获得匹配的方法的集合
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType)); // 抽象方法,子类实现
if (logger.isTraceEnabled()) {
logger.trace("Mapped " + methods.size() + " handler method(s) for " + userType + ": " + methods);
}
// <4> 遍历方法,逐个注册 HandlerMethod
methods.forEach((key, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(key, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
```
- `<1>` 处,获得处理器类型。这里也分成两种情况,和 [「3.3 createHandlerMethod」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 的一样,就不重复赘述了。
- `<2>` 处,调用 `ClassUtils#getUserClass(handlerType)` 方法获得真实的类。因为handlerType 可能是代理类。代码如下:
```
// ClassUtils.java
public static Class<?> getUserClass(Class<?> clazz) {
// 如果 CG_CLASS 代理类,则获取其父类
if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && superclass != Object.class) {
return superclass;
}
}
return clazz;
}
```
- `<3>` 处,获得匹配的方法的集合。通过 `#getMappingForMethod(Method method, Class<?> handlerType)` **抽象**方法,获得方法的 Mapping 信息。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Provide the mapping for a handler method. A method for which no
* mapping can be provided is not a handler method.
* @param method the method to provide a mapping for
* @param handlerType the handler type, possibly a sub-type of the method's
* declaring class
* @return the mapping, or {@code null} if the method is not mapped
*/
@Nullable
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
```
- 该方法在 RequestMappingHandlerMapping 类中实现。详细解析,见 [「5.4 getMappingForMethod」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<4>` 处,遍历方法,逐个调用 `#registerHandlerMethod(Object handler, Method method, T mapping)` 方法,注册 HandlerMethod 。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* 注册 HandlerMethod
*
* Register a handler method and its unique mapping. Invoked at startup for
* each detected handler method.
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
```
- 这样Mapping 和 `handler` + `method` ,就成功注册到 `mappingRegistry` 中。😈 那么,可能比较重要的问题是,究竟 Mapping 是什么?实际上,在 RequestMappingHandlerMapping 中,就是 `@RequestMapping` 解析成的 RequestMappingInfo 对象。淡定淡定,继续细细往下看。
## 3.6 getHandlerInternal
> 艿艿:这个方法,就是我们在 [《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(一)之 AbstractHandlerMapping》](http://svip.iocoder.cn/Spring-MVC/HandlerMapping-1-AbstractHandlerMapping) 中,看到 AbstractHandlerMapping 给子类暴露的**很核心**的方法。
**实现** `#getHandlerInternal(ServerWebExchange exchange)` 方法,获得请求对应的 HandlerMethod 对象。代码如下:
```
// AbstractHandlerMethodMapping.java
// AbstractHandlerMethodMapping.java
/**
* Look up a handler method for the given request.
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// <1> 获得请求的路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// <2> 获得写锁
this.mappingRegistry.acquireReadLock();
try {
// <3> 获得 HandlerMethod 对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// <4> 进一步,获得 HandlerMethod 对象
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
} finally {
// <5> 释放写锁
this.mappingRegistry.releaseReadLock();
}
}
```
- `<1>` 处,获得请求的路径。
- `<2>` 处,获得读锁。
- `<3>` 处,调用 `#lookupHandlerMethod(ServerWebExchange exchange)` 方法,获得 HandlerMethod 对象。详细解析,见 [「3.6.1 lookupHandlerMethod」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<4>` 处,如果获得到 HandlerMethod 对象,则调用 `HandlerMethod#createWithResolvedBean()` 方法,进一步,获得 HandlerMethod 对象。代码如下:
```
// HandlerMethod.java
/**
* If the provided instance contains a bean name rather than an object instance,
* the bean name is resolved before a {@link HandlerMethod} is created and returned.
*/
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
// 如果是 bean 是 String类型则获取对应的 handler 对象。例如bean = userController 字符串获取后handler = UserController 对象
if (this.bean instanceof String) {
Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
// 创建 HandlerMethod 对象
return new HandlerMethod(this, handler);
}
```
- 比较简单,胖友自己瞅瞅。
- `<5>` 处,释放读锁。
### 3.6.1 lookupHandlerMethod
`#lookupHandlerMethod(ServerWebExchange exchange)` 方法,获得 HandlerMethod 对象。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// <1> Match 数组,存储匹配上当前请求的结果
List<Match> matches = new ArrayList<>();
// <1.1> 优先,基于直接 URL 的 Mapping 们,进行匹配
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
// <1.2> 其次,扫描注册表的 Mapping 们,进行匹配
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// <2> 如果匹配到,则获取最佳匹配的 Match 对象的 handlerMethod 属性
if (!matches.isEmpty()) {
// <2.1> 创建 MatchComparator 对象,排序 matches 结果
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
// <2.2> 获得首个 Match 对象
Match bestMatch = matches.get(0);
// <2.3> 处理存在多个 Match 对象的情况!!
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
// TODO 1012 cors
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
// 比较 bestMatch 和 secondBestMatch ,如果相等,说明有问题,抛出 IllegalStateException 异常
// 因为,两个优先级一样高,说明无法判断谁更优先
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
// <2.4> 处理首个 Match 对象
handleMatch(bestMatch.mapping, lookupPath, request);
// <2.5> 返回首个 Match 对象的 handlerMethod 属性
return bestMatch.handlerMethod;
// <3> 如果匹配不到,则处理不匹配的情况
} else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
```
- `<1>` 处Match 数组存储匹配上当前请求的结果。其中Match 是 AbstractHandlerMethodMapping 的内部类,代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* A thin wrapper around a matched HandlerMethod and its mapping, for the purpose of
* comparing the best match with a comparator in the context of the current request.
*/
private class Match {
/**
* Mapping 对象
*/
private final T mapping;
/**
* HandlerMethod 对象
*/
private final HandlerMethod handlerMethod;
public Match(T mapping, HandlerMethod handlerMethod) {
this.mapping = mapping;
this.handlerMethod = handlerMethod;
}
@Override
public String toString() {
return this.mapping.toString();
}
}
```
- 将 `mapping` 和 `handlerMethod` 封装在一起的对象。
- `<1.1>` 处,优先,基于**直接 URL 的 Mapping 们**,进行匹配。
- `<1.2>` 处,其次,基于**扫描注册表的 Mapping 们**,进行匹配。
- 上述 `<1.1>` 和 `<1.2>` 处,都会调用 `#addMatchingMappings(Collection<T> mappings, List<Match> matches, ServerWebExchange exchange)` 方法,将当前请求和注册表中的 Mapping 进行匹配。若匹配成功,则生成 Mapping 记录,添加到 `matches` 中。代码如下:
```
// AbstractHandlerMethodMapping.java
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, ServerWebExchange exchange) {
// 遍历 Mapping 数组
for (T mapping : mappings) {
// <1> 执行匹配
T match = getMatchingMapping(mapping, exchange);
// <2> 如果匹配,则创建 Match 对象,添加到 matches 中
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
```
- `<1>` 处,遍历 Mapping 数组,逐个调用 `#getMatchingMapping(T mapping, ServerWebExchange exchange)` **抽象**方法,执行匹配。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Check if a mapping matches the current request and return a (potentially
* new) mapping with conditions relevant to the current request.
* @param mapping the mapping to get a match for
* @param exchange the current exchange
* @return the match, or {@code null} if the mapping doesn't match
*/
@Nullable
protected abstract T getMatchingMapping(T mapping, ServerWebExchange exchange);
```
- 如果匹配,则返回的结果**非空**。
- 该方法在 RequestMappingInfoHandlerMapping 类中实现。详细解析,见 [「5.4 getMappingForMethod」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<2>` 处,如果匹配,则创建 Match 对象,添加到 `matches` 中。
- `<2>` 处,如果匹配到,则获取**最佳匹配**的 Match 对象的 `handlerMethod` 属性。
- `<2.1>` 处,创建 MatchComparator 对象,排序 `matches` 结果。其中 MatchComparator 实现 Comparator 接口,是 AbstractHandlerMethodMapping 的私有类Match 排序器。代码如下:
```
// AbstractHandlerMethodMapping.java
private class MatchComparator implements Comparator<Match> {
private final Comparator<T> comparator;
public MatchComparator(Comparator<T> comparator) {
this.comparator = comparator;
}
@Override
public int compare(Match match1, Match match2) {
return this.comparator.compare(match1.mapping, match2.mapping);
}
}
```
- 重点在于 `#compare(Match match1, Match match2)` 方法的实现。通过 `mapping` 属性,即 Mapping 对象。详细解析,见 [「4.2 RequestMappingInfo」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 。
- `<2.2>` 处,获得**首个** Match 对象 `bestMatch` 变量。
- `<2.3>` 处,处理存在多个 Match 对象的情况!!获得**第二** Match 对象 `secondBestMatch` 变量。通过 `bestMatch` 和 `secondBestMatch` 的比较,如果一样的优先级,则抛出 IllegalStateException 异常。因为,两个优先级一样高,说明无法判断谁更优先。
- `<2.4>` 处,调用 `#handleMatch(T mapping, String lookupPath, HttpServletRequest request)` 方法,设置匹配的路径 `lookupPath` 到请求属性种。代码如下:
```
// AbstractHandlerMethodMapping.java
protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
}
```
- 实际上,这个方法,在 RequestMappingInfoHandlerMapping 中,会被重写。详细解析,见 [「5. RequestMappingInfoHandlerMapping」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 中。
- `<2.5>` 处,返回**首个** Match 对象的 `handlerMethod` 属性,即它是最匹配的。
- `<3>` 处,如果匹配不到,调用 `#handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)` 方法,处理不匹配的情况。代码如下:
```
// AbstractHandlerMethodMapping.java
/**
* Invoked when no matching mapping is not found.
* @param mappings all registered mappings
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @throws ServletException in case of errors
*/
@Nullable
protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
throws Exception {
return null;
}
```
- 目前是空实现,在 RequestMappingInfoHandlerMapping 中,会被重写。详细解析,见 [「5. RequestMappingInfoHandlerMapping」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 中。
# 4. RequestMappingInfoHandlerMapping
`org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping` ,继承 AbstractHandlerMethodMapping 抽象类,定义了使用的泛型 `<T>` 为 `org.springframework.web.servlet.mvc.method.RequestMappingInfo` 类,即 Mapping 类型就是 RequestMappingInfo 。
**这样有什么好处呢**RequestMappingInfoHandlerMapping 定义了使用了 RequestMappingInfo 对象,而 其子类 RequestMappingHandlerMapping 使用了 `@RequestMapping` 注解,来生成 RequestMappingInfo 对象。这样,如果未来我们自己定义了自己的注解,或者其他方式来生成 RequestMappingHandlerMapping 对象,未尝不可。关于这点,胖友可以再仔细思考下。
- [「3. AbstractHandlerMethodMapping」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 的定位
- [「4. RequestMappingInfoHandlerMapping」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 的定位
- [「5. RequestMappingHandlerMapping」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 的定位
## 4.1 构造方法
```
// RequestMappingInfoHandlerMapping.java
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
protected RequestMappingInfoHandlerMapping() {
setHandlerMethodMappingNamingStrategy(new RequestMappingInfoHandlerMethodMappingNamingStrategy());
}
}
```
- `<T>` 泛型,为 RequestMappingInfo 类型。
- 设置了 `AbstractHandlerMethodMapping.namingStrategy` 属性,为 RequestMappingInfoHandlerMethodMappingNamingStrategy 对象。关于它,在 [「3.1 构造方法」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 已经介绍了。
## 4.2 RequestMappingInfo
> RequestMappingInfo 不是 RequestMappingInfoHandlerMapping 的内部类,但是是 **RequestMappingInfo**-HandlerMapping 的**前缀**。
`org.springframework.web.servlet.mvc.method.RequestMappingInfo` ,实现 RequestCondition 接口,请求匹配信息。
> 关于 `org.springframework.web.servlet.mvc.condition.RequestCondition` ,请求条件接口,定义了三个方法,分别是
>
> - `#combine(T other)` 合并方法
> - `#getMatchingCondition(HttpServletRequest request)` 匹配方法
> - `#compareTo(T other, HttpServletRequest request)` 比较方法
>
> 原代码,胖友点击 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestCondition.java) 查看。
### 4.2.1 构造方法
```
// RequestMappingInfo.java
/**
* 名字
*/
@Nullable
private final String name;
/**
* 请求路径的条件
*/
private final PatternsRequestCondition patternsCondition;
/**
* 请求方法的条件
*/
private final RequestMethodsRequestCondition methodsCondition;
/**
* 参数的条件
*/
private final ParamsRequestCondition paramsCondition;
/**
* 请求头的条件
*/
private final HeadersRequestCondition headersCondition;
/**
* 可消费的 Content-Type 的条件
*/
private final ConsumesRequestCondition consumesCondition;
/**
* 可生产的 Content-Type 的条件
*/
private final ProducesRequestCondition producesCondition;
/**
* 自定义的条件
*/
private final RequestConditionHolder customConditionHolder;
```
- 这里,我们可以看到各种条件。实际上,和 `@RequestMapping` 注解是一一对应的。所以,每个属性的详细解释,胖友也可以看看 [《使用 @RequestMapping 注解映射请求路径》](https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-3/2-mapping-requests-with-@requestmapping.html) 。
- 😈 实际上,我们日常使用最多的还是 `patternsCondition` 请求路径条件,和 `methodsCondition` 请求方法条件。
- RequestCondition 的整体类图如下,
![RequestCondition 类图](http://static.iocoder.cn/images/2022-03-07/01.png)
RequestCondition 类图
- 还是比较多的,本文我们只会简单介绍 PatternsRequestCondition 的实现。
- 😈 Google 了一圈,是不是已经有人详细写了 RequestCondition 的解析,发现还没人细写。说明,可能都不太感兴趣,哈哈哈哈。
### 4.2.2 getMatchingCondition
`#getMatchingCondition(HttpServletRequest request)` 方法,从当前 RequestMappingInfo 获得匹配的条件。如果匹配,则基于其匹配的条件,创建**新的** RequestMappingInfo 对象。如果不匹配,则返回 `null` 。代码如下:
```
// RequestMappingInfo.java
@SuppressWarnings("Duplicates")
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
// 匹配 methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition
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);
// 如果任一为空,则返回 null ,表示匹配失败
if (methods == null || params == null || headers == null || consumes == null || produces == null) {
return null;
}
// 匹配 patternsCondition
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) { // 如果 patterns 为空,则返回 null ,表示匹配失败
return null;
}
// 匹配 customConditionHolder
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) { // 如果 custom 为空,则返回 null ,表示匹配失败
return null;
}
// 创建匹配的 RequestMappingInfo 对象。
// 为什么要创建 RequestMappingInfo 对象呢?因为当前 RequestMappingInfo 对象,一个 methodsCondition 可以配置 GET、POST、DELETE 等等条件,但是实际就匹配一个请求类型,此时 methods 只代表其匹配的那个。
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
```
- 虽然代码非常长,实际都是调用每个属性对应的 `#getMatchingCondition(HttpServletRequest request)` 方法,获得其匹配的**真正的**条件。
- 那么可能胖友会疑惑,如果一个 `@RequestMapping(value = "user/login")` 注解,并未写 **RequestMethod** 的条件,岂不是会报空?实际上不会。在这种情况下,会创建一个 RequestMethodsRequestCondition 对象,并且在匹配时,直接返回自身。代码如下:
```
// RequestMappingInfo.java
/**
* RequestMethod 集合
*/
private final Set<RequestMethod> methods;
@Override
@Nullable
public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
if (CorsUtils.isPreFlightRequest(request)) {
return matchPreFlight(request);
}
// 空的情况下,就返回自身
if (getMethods().isEmpty()) {
if (RequestMethod.OPTIONS.name().equals(request.getMethod()) &&
!DispatcherType.ERROR.equals(request.getDispatcherType())) {
return null; // No implicit match for OPTIONS (we handle it)
}
return this;
}
// 非空,逐个匹配
return matchRequestMethod(request.getMethod());
}
```
```
* 也就是说,没有 **RequestMethod** 的条件,一定匹配成功,且结果就是自身 RequestMethodsRequestCondition 对象。
```
- 这个方法,详细的代码,胖友自己瞅瞅落。
### 4.2.3 compareTo
`#compareTo(RequestMappingInfo other, HttpServletRequest request)` 方法,比较优先级。代码如下:
```
// RequestMappingInfo.java
/**
* 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.
*/
@Override
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result;
// Automatic vs explicit HTTP HEAD mapping
// 针对 HEAD 请求方法,特殊处理
if (HttpMethod.HEAD.matches(request.getMethod())) {
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
}
// 比较 patternsCondition
result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
if (result != 0) {
return result;
}
// 比较 paramsCondition
result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
if (result != 0) {
return result;
}
// 比较 headersCondition
result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
if (result != 0) {
return result;
}
// 比较 consumesCondition
result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
if (result != 0) {
return result;
}
// 比较 producesCondition
result = this.producesCondition.compareTo(other.getProducesCondition(), request);
if (result != 0) {
return result;
}
// Implicit (no method) vs explicit HTTP method mappings
// 比较 methodsCondition
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
// 比较 customConditionHolder
result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
if (result != 0) {
return result;
}
return 0;
}
```
- 虽然代码非常长,实际都是按照优先级,逐个调用每个属性对应的 `#compareTo(RequestMethodsRequestCondition other, HttpServletRequest request)` 方法,直到比到**不相等**。
### 4.2.4 PatternsRequestCondition
`org.springframework.web.servlet.mvc.condition.PatternsRequestCondition` ,继承 AbstractRequestCondition 抽象类,请求路径条件。
这是最重要的 RequestCondition 实现类,所以胖友一定要看看。😈 当然,艿艿这里先偷懒下,胖友可以看看 [《详解 SpringMVC 请求的时候是如何找到正确的 Controller [附带源码分析\]》](https://www.cnblogs.com/fangjian0423/p/springMVC-request-mapping.html) 的 [「PatternsRequestCondition」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 部分。
- 当然,艿艿还是写了 PatternsRequestCondition 的简单注释。
## 4.3 getMappingPathPatterns
`#getMappingPathPatterns(RequestMappingInfo info)` 方法,获得 Mapping 对应的请求路径集合。代码如下:
```
// RequestMappingInfoHandlerMapping.java
/**
* Get the URL path patterns associated with this {@link RequestMappingInfo}.
*/
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
return info.getPatternsCondition().getPatterns();
}
```
- 该方法,在 [「3.2.2 register」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 中,看到对该方法的调用。
> 艿艿:因为担心调用栈太深,文章无法排版。所以文章相对来说,按照类的维度来写。所以,一些方法的解释,胖友一定要结合着看看。😈 最好,边看,边结合 IDEA 调试。
## 4.4 getMatchingMapping
`#getMatchingMapping(RequestMappingInfo info, HttpServletRequest request)` 方法,获得请求对应的 RequestMappingInfo 对象。代码如下:
```
// RequestMappingInfoHandlerMapping.java
/**
* Check if the given RequestMappingInfo matches the current request and
* return a (potentially new) instance with conditions that match the
* current request -- for example with a subset of URL patterns.
* @return an info in case of a match; or {@code null} otherwise.
*/
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
```
- 方法内部,调用的就是 [「4.2.2 getMatchingCondition」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 的方法。
- 该方法,在 [「3.6.1 lookupHandlerMethod」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 中,看到对该方法的调用。
## 4.5 handleMatch
`#handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request)` 方法,覆写父类的方法,设置更多的属性,到请求中。代码如下:
```
// RequestMappingInfoHandlerMapping.java
/**
* Expose URI template variables, matrix variables, and producible media types in the request.
* @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
* @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
*/
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
// 获得 bestPattern 和 uriVariables
String bestPattern; // 最佳路径
Map<String, String> uriVariables; // 路径上的变量集合
Set<String> patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
bestPattern = lookupPath;
uriVariables = Collections.emptyMap();
} else {
bestPattern = patterns.iterator().next();
uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
}
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
// 设置 MATRIX_VARIABLES_ATTRIBUTE 属性,到请求中
if (isMatrixVariableContentAvailable()) {
Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
}
// 设置 URI_TEMPLATE_VARIABLES_ATTRIBUTE 属性,到请求中
Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
// 设置 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性,到请求中
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
```
这个方法,会调用如下两个方法,感兴趣的胖友,自己研究下:
- `isMatrixVariableContentAvailable()` 方法
- `extractMatrixVariables(HttpServletRequest request, Map<String, String> uriVariables)` 方法
## 4.6 handleNoMatch
`#handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request)` 方法,覆写父类方法,处理无匹配 Mapping 的情况。主要用途是,给出为什么找不到 Mapping 的原因。代码如下:
```
// RequestMappingInfoHandlerMapping.java
/**
* Iterate all RequestMappingInfo's once again, look if any match by URL at
* least and raise exceptions according to what doesn't match.
* @throws HttpRequestMethodNotSupportedException if there are matches by URL
* but not by HTTP method
* @throws HttpMediaTypeNotAcceptableException if there are matches by URL
* but not by consumable/producible media types
*/
@Override
protected HandlerMethod handleNoMatch(
Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
// <1> 创建 PartialMatchHelper 对象,解析可能的错误
PartialMatchHelper helper = new PartialMatchHelper(infos, request);
if (helper.isEmpty()) {
return null;
}
// <2> 方法错误
if (helper.hasMethodsMismatch()) {
Set<String> methods = helper.getAllowedMethods();
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
HttpOptionsHandler handler = new HttpOptionsHandler(methods);
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
}
throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
}
// 可消费的 Content-Type 错误
if (helper.hasConsumesMismatch()) {
Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
MediaType contentType = null;
if (StringUtils.hasLength(request.getContentType())) {
try {
contentType = MediaType.parseMediaType(request.getContentType());
} catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
}
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
}
// 可生产的 Content-Type 错误
if (helper.hasProducesMismatch()) {
Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
}
// <5> 参数错误
if (helper.hasParamsMismatch()) {
List<String[]> conditions = helper.getParamConditions();
throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
}
return null;
}
```
- `<1>` 处,核心代码在 PartialMatchHelper 中实现,当然 😈 本文还是不详细解析,感兴趣的朋友,自己瞅瞅。
- `<2>` 处,方法错误。这是一个非常常见的错误,例如说 `POST user/login` 存在,但是我们请求了 `GET user/login` 。
- `<5>` 处,参数错误。
- 如果胖友想要对这个方法抛出的异常,作进一步的处理,可以参考 [《Spring Boot 全局异常处理及自定义异常类》](https://blog.csdn.net/qq_34083066/article/details/79424142) 。
# 5. RequestMappingHandlerMapping
`org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping`,实现 MatchableHandlerMapping 接口、EmbeddedValueResolverAware 接口,继承 RequestMappingInfoHandlerMapping 抽象类,基于 `@RequestMapping` 注解来构建 RequestMappingInfo 对象。
## 5.1 构造方法
```
// RequestMappingHandlerMapping.java
private boolean useSuffixPatternMatch = true;
private boolean useRegisteredSuffixPatternMatch = false;
private boolean useTrailingSlashMatch = true;
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
@Nullable
private StringValueResolver embeddedValueResolver;
/**
* RequestMappingInfo.BuilderConfiguration 配置
*/
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
```
- 如果胖友想设置如上的值,可以参考 [《Spring mvc autowire RequestMappingHandlerMapping》](https://stackoverflow.com/questions/31893335/spring-mvc-autowire-requestmappinghandlermapping) 。
## 5.2 afterPropertiesSet
```
// RequestMappingHandlerMapping.java
@Override
public void afterPropertiesSet() {
// 构建 RequestMappingInfo.BuilderConfiguration 对象
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 调用父类,初始化
super.afterPropertiesSet();
}
```
## 5.3 isHandler
> 艿艿:😈 看到此处,有没很感动的感觉。
实现 `#isHandler(Class<?> beanType)` 方法,判断是否为处理器。代码如下:
```
// RequestMappingHandlerMapping.java
/**
* {@inheritDoc}
* <p>Expects a handler to have either a type-level @{@link Controller}
* annotation or a type-level @{@link RequestMapping} annotation.
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
```
## 5.4 getMappingForMethod
> 艿艿:😈 看到此处,有没更感动的感觉。
`#getMappingForMethod(Method method, Class<?> handlerType)` 方法,获得方法上的 RequestMappingInfo 对象,基于 `@RequestMapping` 构造。代码如下:
```
// RequestMappingHandlerMapping.java
/**
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
* @return the created RequestMappingInfo, or {@code null} if the method
* does not have a {@code @RequestMapping} annotation.
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// <1> 基于方法上的 @RequestMapping 注解,创建 RequestMappingInfo 对象
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// <2> 基于类上的 @RequestMapping 注解,合并进去
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
// <3> 如果有前缀,则设置到 info 中
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
```
- `<1>` 处,调用 `#createRequestMappingInfo(AnnotatedElement element)` 方法,基于方法上的 `@RequestMapping` 注解,创建 RequestMappingInfo 对象。代码如下:
```
// RequestMappingHandlerMapping.java
/**
* Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
* supplying the appropriate custom {@link RequestCondition} depending on whether
* the supplied {@code annotatedElement} is a class or method.
* @see #getCustomTypeCondition(Class)
* @see #getCustomMethodCondition(Method)
*/
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// <1> 获得 @RequestMapping 注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// <2> 获得自定义的条件。目前都是空方法,可以无视
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// <3> 基于 @RequestMapping 注解,创建 RequestMappingInfo 对象
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
```
- `<1>` 处,获得 `@RequestMapping` 注解。当然,`@GetMapping`、`@PostMapping` 等等注解,也可以被扫描到。
- `<2>` 处,获得自定义的条件。目前都是空方法,可以无视。代码如下:
```
// RequestMappingHandlerMapping.java
/**
* Provide a custom type-level request condition.
* The custom {@link RequestCondition} can be of any type so long as the
* same condition type is returned from all calls to this method in order
* to ensure custom request conditions can be combined and compared.
* <p>Consider extending {@link AbstractRequestCondition} for custom
* condition types and using {@link CompositeRequestCondition} to provide
* multiple custom conditions.
* @param handlerType the handler type for which to create the condition
* @return the condition, or {@code null}
*/
@Nullable
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return null;
}
/**
* Provide a custom method-level request condition.
* The custom {@link RequestCondition} can be of any type so long as the
* same condition type is returned from all calls to this method in order
* to ensure custom request conditions can be combined and compared.
* <p>Consider extending {@link AbstractRequestCondition} for custom
* condition types and using {@link CompositeRequestCondition} to provide
* multiple custom conditions.
* @param method the handler method for which to create the condition
* @return the condition, or {@code null}
*/
@Nullable
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return null;
}
```
- 可以暂时无视。
- `<3>` 处,调用 `#createRequestMappingInfo(RequestMapping requestMapping, RequestCondition<?> customCondition)` 方法,基于 `@RequestMapping` 注解,创建 RequestMappingInfo 对象。代码如下:
```
// RequestMappingHandlerMapping.java
/**
* Create a {@link RequestMappingInfo} from the supplied
* {@link RequestMapping @RequestMapping} annotation, which is either
* a directly declared annotation, a meta-annotation, or the synthesized
* result of merging annotation attributes within an annotation hierarchy.
*/
protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// 创建 RequestMappingInfo.Builder 对象,设置对应属性
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
// 创建 RequestMappingInfo 对象
return builder.options(this.config).build();
}
```
- 关于 RequestMappingInfo.Builder 类,胖友自己可以简单看看。
- `<2>` 处,基于类上的 `@RequestMapping` 注解,创建 RequestMappingInfo 对象,并合并进去。
- `<3>` 处,如果有前缀,则设置到 `info` 中。
## 5.5 match
`#match(HttpServletRequest request, String pattern)` 方法,执行匹配。代码如下:
```
// RequestMappingHandlerMapping.java
@Override
public RequestMatchResult match(HttpServletRequest request, String pattern) {
// 创建 RequestMappingInfo 对象
RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build();
// 获得匹配的 RequestMappingInfo 对象
RequestMappingInfo matchingInfo = info.getMatchingCondition(request);
if (matchingInfo == null) { // 如果不匹配,则返回 null
return null;
}
// 获得请求路径的集合
Set<String> patterns = matchingInfo.getPatternsCondition().getPatterns();
// 获得请求的路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 创建 RequestMatchResult 结果
return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher());
}
```
# 666. 彩蛋
卧槽,又好长一篇。有点吐血了!!!!!
参考和推荐如下文章:
- 韩路彪 [《看透 Spring MVC源代码分析与实践》](https://item.jd.com/11807414.html) 的 [「12.3 AbstractHandlerMethodMapping 系列」](https://svip.iocoder.cn/Spring-MVC/HandlerMapping-3-AbstractHandlerMethodMapping/#) 小节