Skip to content

Commit ce9e4bc

Browse files
committed
spring-mvc: 处理器查找
1 parent 575fc0e commit ce9e4bc

File tree

7 files changed

+947
-9
lines changed

7 files changed

+947
-9
lines changed
12.1 KB
Loading
21.6 KB
Loading
12.4 KB
Loading
70.7 KB
Loading

note/spring-mvc.md

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ initFlashMapManager方法会向容器注册SessionFlashMapManager对象,类图
409409

410410
## HandlerMapping初始化
411411

412-
此接口用以根据请求的URL寻找合适的处理器。从前面配置解析一节可以看出,我们的容器中有三个HandlerMapping实现,下面进行分别说明
412+
此接口用以根据请求的URL寻找合适的处理器。从前面配置解析一节可以看出,我们的容器中有三个HandlerMapping实现,下面以RequestMappingHandlerMapping位代表进行说明
413413

414414
### RequestMappingHandlerMapping
415415

@@ -435,6 +435,7 @@ protected void initHandlerMethods() {
435435
}
436436
}
437437
}
438+
//空实现
438439
handlerMethodsInitialized(getHandlerMethods());
439440
}
440441
```
@@ -453,6 +454,7 @@ detectHandlerMethods方法将反射遍历类中所有的public方法,如果方
453454

454455
```java
455456
public void register(T mapping, Object handler, Method method) {
457+
//包装bean和方法
456458
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
457459
this.mappingLookup.put(mapping, handlerMethod);
458460
List<String> directUrls = getDirectUrls(mapping);
@@ -472,7 +474,113 @@ public void register(T mapping, Object handler, Method method) {
472474
}
473475
```
474476

477+
mapping其实是一个RequestMappingInfo对象,可以将其看做是**@RequestMapping注解各种属性的一个封装**。最终由RequestMappingInfo.createRequestMappingInfo方法创建,源码:
475478

479+
```java
480+
protected RequestMappingInfo createRequestMappingInfo(
481+
RequestMapping requestMapping, RequestCondition<?> customCondition) {
482+
return RequestMappingInfo
483+
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
484+
.methods(requestMapping.method())
485+
.params(requestMapping.params())
486+
.headers(requestMapping.headers())
487+
.consumes(requestMapping.consumes())
488+
.produces(requestMapping.produces())
489+
.mappingName(requestMapping.name())
490+
.customCondition(customCondition)
491+
.options(this.config)
492+
.build();
493+
}
494+
```
495+
496+
这就很明显了,具体每种属性什么意义可以参考@RequestMapping源码。
497+
498+
register方法中urlLookup其实就是将paths属性中的每个path都与处理器做映射。
499+
500+
getNamingStrategy方法得到的是一个HandlerMethodMappingNamingStrategy接口的实例,此接口用以根据HandlerMethod得到一个名字,类图:
501+
502+
![HandlerMethodMappingNamingStrategy类图](images/HandlerMethodMappingNamingStrategy.jpg)
503+
504+
比如对于我们的控制器,SimpleController.echo方法,最终得到的名字将是SC#echo。
505+
506+
#### 跨域请求
507+
508+
spring-mvc自4.2开启加入了跨域请求Cors的支持,主要有两种配置方式:
509+
510+
- xml:
511+
512+
```xml
513+
<mvc:cors>
514+
<mvc:mapping path=""/>
515+
</mvc:cors>
516+
```
517+
518+
- @CrossOrigin注解。
519+
520+
Cors的原理可以参考:
521+
522+
[探讨跨域请求资源的几种方式](http://www.cnblogs.com/dojo-lzz/p/4265637.html)
523+
524+
而initCorsConfiguration方法的作用便是将@CrossOrigin注解的各种属性封装在CorsConfiguration中。
525+
526+
## HandlerAdapter初始化
527+
528+
同样,我们以RequestMappingHandlerAdapter为例进行说明,类图:
529+
530+
![RequestMappingHandlerAdapter类图](images/RequestMappingHandlerAdapter.jpg)
531+
532+
显然,入口在afterPropertiesSet方法:
533+
534+
```java
535+
@Override
536+
public void afterPropertiesSet() {
537+
// Do this first, it may add ResponseBody advice beans
538+
initControllerAdviceCache();
539+
if (this.argumentResolvers == null) {
540+
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
541+
this.argumentResolvers = new HandlerMethodArgumentResolverComposite()
542+
.addResolvers(resolvers);
543+
}
544+
if (this.initBinderArgumentResolvers == null) {
545+
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
546+
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite()
547+
.addResolvers(resolvers);
548+
}
549+
if (this.returnValueHandlers == null) {
550+
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
551+
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite()
552+
.addHandlers(handlers);
553+
}
554+
}
555+
```
556+
557+
### @ControllerAdvice
558+
559+
initControllerAdviceCache方法用以解析并存储标注了@ControllerAdvice的bean,这东西是干什么的参考:
560+
561+
[Spring3.2新注解@ControllerAdvice](http://jinnianshilongnian.iteye.com/blog/1866350)
562+
563+
### 参数解析器
564+
565+
HandlerMethodArgumentResolver即参数解析器,负责从request中解析、得到Controller方法所需的参数。afterPropertiesSet方法设置了一组默认的解析器。具体是哪些参考getDefaultArgumentResolvers方法。类图:
566+
567+
![HandlerMethodArgumentResolver类图](images/HandlerMethodArgumentResolver.jpg)
568+
569+
### @InitBinder支持
570+
571+
此注解定义的其实是自定义类型转换器。使用方法参考:
572+
573+
[springMVC @initBinder 使用](http://blog.csdn.net/songzaiblog/article/details/49757253)
574+
575+
getDefaultInitBinderArgumentResolvers返回了一组默认使用的转换器,不过其实这里的转换器和上面的参数解析器其实是一个类型的,这里留个坑。
576+
577+
### 返回结果解析器
578+
579+
HandlerMethodReturnValueHandler接口用以处理方法调用(Controller方法)的返回值,类图:
580+
581+
![HandlerMethodReturnValueHandler类图](images/HandlerMethodReturnValueHandler.jpg)
582+
583+
getDefaultReturnValueHandlers方法便返回了一坨这东西。
476584

477585
# 请求响应
478586

@@ -507,5 +615,53 @@ Spring MVC会在请求分发之前进行上下文的准备工作,含两部分:
507615

508616
## 请求分发
509617

618+
DispatcherServlet.doDispatch简略版源码:
619+
620+
```java
621+
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
622+
HandlerExecutionChain mappedHandler = getHandler(processedRequest);
623+
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
624+
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
625+
applyDefaultViewName(processedRequest, mv);
626+
mappedHandler.applyPostHandle(processedRequest, response, mv);
627+
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
628+
}
629+
```
510630

631+
### 处理器查找
632+
633+
即为请求寻找合适的Controller的过程。DispatcherServlet.getHandler:
634+
635+
```java
636+
protected HandlerExecutionChain getHandler(HttpServletRequest request) {
637+
for (HandlerMapping hm : this.handlerMappings) {
638+
HandlerExecutionChain handler = hm.getHandler(request);
639+
if (handler != null) {
640+
return handler;
641+
}
642+
}
643+
return null;
644+
}
645+
```
646+
647+
从这里可以看出,寻找处理器实际上委托给HandlerMapping实现,寻找的过程便是遍历所有的HandlerMapping进行查找,**一旦找到,那么不再继续进行遍历**。也就是说HandlerMapping之间有优先级的概念,而根据AnnotationDrivenBeanDefinitionParser的注释,RequestMappingHandlerMapping其实有最高的优先级。
648+
649+
AbstractHandlerMapping.getHandler:
650+
651+
```java
652+
@Override
653+
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
654+
Object handler = getHandlerInternal(request);
655+
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
656+
if (CorsUtils.isCorsRequest(request)) {
657+
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
658+
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
659+
CorsConfiguration config = (globalConfig != null ?
660+
globalConfig.combine(handlerConfig) : handlerConfig);
661+
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
662+
}
663+
return executionChain;
664+
}
665+
```
511666

667+
getHandlerInternal方法便是根据url进行查找的过程,可以参见MVC初始化-HandlerMapping初始化一节。下面重点是执行链的生成。

0 commit comments

Comments
 (0)