Skip to content

Commit 575fc0e

Browse files
committed
spring-mvc: HanderMapping初始化
1 parent 24e8d00 commit 575fc0e

File tree

4 files changed

+643
-2
lines changed

4 files changed

+643
-2
lines changed

note/images/MappingRegistry.jpg

18.8 KB
Loading
55.5 KB
Loading

note/spring-mvc.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,73 @@ initFlashMapManager方法会向容器注册SessionFlashMapManager对象,类图
407407

408408
此接口和FlashMap搭配使用,用于在**请求重定向时保存/传递参数**
409409

410+
## HandlerMapping初始化
411+
412+
此接口用以根据请求的URL寻找合适的处理器。从前面配置解析一节可以看出,我们的容器中有三个HandlerMapping实现,下面进行分别说明。
413+
414+
### RequestMappingHandlerMapping
415+
416+
此实现根据@Controller@RequestMapping注解完成解析。类图(忽略部分接口):
417+
418+
![RequestMappingHandlerMapping类图](images/RequestMappingHandlerMapping.jpg)
419+
420+
初始化的入口位于AbstractHandlerMethodMapping的afterPropertiesSet方法,afterPropertiesSet调用了initHandlerMethods:
421+
422+
```java
423+
protected void initHandlerMethods() {
424+
//获取容器中所有的bean
425+
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
426+
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :getApplicationContext().getBeanNamesForType(Object.class));
427+
for (String beanName : beanNames) {
428+
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
429+
Class<?> beanType = null;
430+
beanType = getApplicationContext().getType(beanName);
431+
//isHandler方法的原理:
432+
//判断类上有没有@Controller注解或者是@RequestMapping注解
433+
if (beanType != null && isHandler(beanType)) {
434+
detectHandlerMethods(beanName);
435+
}
436+
}
437+
}
438+
handlerMethodsInitialized(getHandlerMethods());
439+
}
440+
```
441+
442+
detectHandlerMethods方法将反射遍历类中所有的public方法,如果方法上含有@RequestMapping注解,那么将方法上的路径与类上的基础路径(如果有)进行合并,之后将映射(匹配关系)注册到MappingRegistry中。
443+
444+
注意,**类上的@RequestMapping注解只能作为基路径存在,也就是说,如果类里面没有任何的方法级@RequestMapping注解,那么类上的注解是没有意义的**。这一点可以从实验和源码上得到证实。
445+
446+
下面我们关注一下映射关系是如何保存(注册)的。
447+
448+
内部类AbstractHandlerMethodMapping.MappingRegistry是映射的载体,类图:
449+
450+
![MappingRegistry类图](images/MappingRegistry.jpg)
451+
452+
其register方法简略版源码:
453+
454+
```java
455+
public void register(T mapping, Object handler, Method method) {
456+
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
457+
this.mappingLookup.put(mapping, handlerMethod);
458+
List<String> directUrls = getDirectUrls(mapping);
459+
for (String url : directUrls) {
460+
this.urlLookup.add(url, mapping);
461+
}
462+
String name = null;
463+
if (getNamingStrategy() != null) {
464+
name = getNamingStrategy().getName(handlerMethod, mapping);
465+
addMappingName(name, handlerMethod);
466+
}
467+
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
468+
if (corsConfig != null) {
469+
this.corsLookup.put(handlerMethod, corsConfig);
470+
}
471+
this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
472+
}
473+
```
474+
475+
476+
410477
# 请求响应
411478

412479
我们先来看一下入口在哪。众所周知,Servlet标准定义了所有请求先由service方法处理,如果是get或post方法,那么再交由doGet或是doPost方法处理。
@@ -431,3 +498,14 @@ Spring要覆盖此方法的目的在于拦截PATCH请求,PATCH请求与PUT类
431498

432499
FrameworkServlet同样也覆盖了doGet和doPost方法,两者只是调用processRequest方法。
433500

501+
## 请求上下文
502+
503+
Spring MVC会在请求分发之前进行上下文的准备工作,含两部分:
504+
505+
1. 将地区(Locale)和请求属性以ThreadLocal的方法与当前线程进行关联,分别可以通过LocaleContextHolder和RequestContextHolder进行获取。
506+
2. 将WebApplicationContext、FlashMap等组件放入到Request属性中。
507+
508+
## 请求分发
509+
510+
511+

0 commit comments

Comments
 (0)