Skip to content

Commit a35e825

Browse files
committed
SpringMVC处理分发HTTP请求
1 parent 563f00d commit a35e825

File tree

3 files changed

+385
-1
lines changed

3 files changed

+385
-1
lines changed

docs/Spring/SpringMVC/SpringMVC的设计与实现.md

Lines changed: 385 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,14 +370,398 @@ HandlerMappings完成对MVC中Controller的定义和配置,只不过在Web这
370370

371371
## 4 SpringMVC处理分发HTTP请求
372372
### 4.1 HandlerMapping的配置和设计原理
373+
前面分析了DispatcherServletSpring MVC框架的初始化过程,在此基础上,我们再进一步分析HandlerMapping的实现原理,看看这个MVC框架中比较关键的控制部分是如何实现的。
373374

375+
在初始化完成时,在上下文环境中已定义的所有HandlerMapping都已经被加载了,这些加载的handlerMappings被放在一个List中并被排序,存储着HTTP请求对应的映射数据。这个List中的每一个元素都对应着一个具体handlerMapping的配置,一般每一个handlerMapping
376+
可以持有一系列从URL请求到Controller的映射,而Spring MVC提供了一系列的HandlerMapping实现。
374377

378+
![avatar](/images/springMVC/HandlerMapping组件.png)
375379

376-
### 4.2 使用HandlerMapping完成请求的映射处理
380+
以SimpleUrlHandlerMapping这个handlerMapping为例来分析HandlerMapping的设计与实现。在SimpleUrlHandlerMapping中,定义了一个map来 持有 一系列的映射关系。通过这些在HandlerMapping中定义的映射关系,即这些URL请求和控制器的对应关系,使Spring MVC
381+
应用可以根据HTTP请求确定一个对应的Controller。具体来说,这些映射关系是通过接口HandlerMapping来封装的,在HandlerMapping接 口中定义了一个getHandler方法,通过这个方法,可以获得与HTTP请求对应的HandlerExecutionChain,在这个HandlerExecutionChain
382+
中,封装了具体的Controller对象。
383+
```java
384+
public interface HandlerMapping {
385+
386+
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
387+
388+
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
389+
390+
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
391+
392+
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
393+
394+
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
395+
396+
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
397+
398+
/**
399+
* 返回的这个HandlerExecutionChain不但持有handler本身,还包括了处理这个HTTP请求的拦截器
400+
*/
401+
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
402+
403+
}
404+
```
405+
这个HandlerExecutionChain的实现看起来比较简洁,它持有一个Interceptor链和一个handler对象,这个handler对象实际上就是HTTP请求对应的Controller,在持有这个handler对象的同时,还在HandlerExecutionChain中设置了一个拦截器链,通过这个拦截器链中的拦截器,
406+
可以为handler对象提供功能的增强。要完成这些工作,需要对拦截器链和handler都进行配置,这些配置都是在HandlerExecutionChain的初始化函数中完成的。为了维护这个拦截器链和handler,HandlerExecutionChain还提供了一系列与拦截器链维护相关的操作,比如,为拦
407+
截器链增加拦截器的addInterceptor()方法。
408+
```java
409+
public class HandlerExecutionChain {
410+
411+
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
412+
413+
private final Object handler;
414+
415+
private HandlerInterceptor[] interceptors;
416+
417+
private List<HandlerInterceptor> interceptorList;
418+
419+
private int interceptorIndex = -1;
420+
421+
422+
public HandlerExecutionChain(Object handler) {
423+
this(handler, null);
424+
}
425+
426+
public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {
427+
if (handler instanceof HandlerExecutionChain) {
428+
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
429+
this.handler = originalChain.getHandler();
430+
this.interceptorList = new ArrayList<HandlerInterceptor>();
431+
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
432+
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
433+
}
434+
else {
435+
this.handler = handler;
436+
this.interceptors = interceptors;
437+
}
438+
}
439+
440+
public Object getHandler() {
441+
return this.handler;
442+
}
443+
444+
/**
445+
* 为拦截器链 添加拦截器
446+
*/
447+
public void addInterceptor(HandlerInterceptor interceptor) {
448+
initInterceptorList();
449+
this.interceptorList.add(interceptor);
450+
}
451+
452+
/**
453+
* 批量添加拦截器
454+
*/
455+
public void addInterceptors(HandlerInterceptor[] interceptors) {
456+
if (interceptors != null) {
457+
initInterceptorList();
458+
this.interceptorList.addAll(Arrays.asList(interceptors));
459+
}
460+
}
461+
462+
/**
463+
* 延迟初始化interceptorList和interceptors集合
464+
*/
465+
private void initInterceptorList() {
466+
if (this.interceptorList == null) {
467+
this.interceptorList = new ArrayList<HandlerInterceptor>();
468+
}
469+
if (this.interceptors != null) {
470+
this.interceptorList.addAll(Arrays.asList(this.interceptors));
471+
this.interceptors = null;
472+
}
473+
}
474+
475+
public HandlerInterceptor[] getInterceptors() {
476+
if (this.interceptors == null && this.interceptorList != null) {
477+
this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
478+
}
479+
return this.interceptors;
480+
}
481+
482+
@Override
483+
public String toString() {
484+
if (this.handler == null) {
485+
return "HandlerExecutionChain with no handler";
486+
}
487+
StringBuilder sb = new StringBuilder();
488+
sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");
489+
if (!CollectionUtils.isEmpty(this.interceptorList)) {
490+
sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");
491+
if (this.interceptorList.size() > 1) {
492+
sb.append("s");
493+
}
494+
}
495+
return sb.toString();
496+
}
497+
}
498+
```
499+
HandlerExecutionChain中定义的Handler和Interceptor需要在定义HandlerMapping时配置好,例如对具体的SimpleURLHandlerMapping,要做的就是根据URL映射的方式,注册Handler和Interceptor,从而维护一个反映这种映射关系的handlerMap。当需要匹配HTTP请求时,需要查询这个handlerMap中的信息来得到对应的HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程,这个注册过程在容器对Bean进行依赖注入时发生,它实际上是通过一个Bean的postProcessor()来完成的。以SimpleHandlerMapping为例,需要注意的是,这里用到了对容器的回调,只有SimpleHandlerMapping是ApplicationContextAware的子类才能启动这个注册过程。这个注册过程完成的是反映URL和Controller之间映射关系的handlerMap的建立。
500+
501+
![avatar](/images/springMVC/SimpleUrlHandlerMapping的继承关系.png)
502+
503+
```java
504+
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
505+
@Override
506+
public void initApplicationContext() throws BeansException {
507+
super.initApplicationContext();
508+
registerHandlers(this.urlMap);
509+
}
510+
511+
/**
512+
* 为相应的路径注册URL映射中指定的所有handlers处理程序
513+
*/
514+
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
515+
if (urlMap.isEmpty()) {
516+
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
517+
}
518+
else {
519+
// 这里对bean的配置进行解析,然后调用父类的registerHandler()方法进行解析
520+
for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
521+
String url = entry.getKey();
522+
Object handler = entry.getValue();
523+
// 如果url没有斜线,就在前面加上斜线
524+
if (!url.startsWith("/")) {
525+
url = "/" + url;
526+
}
527+
// Remove whitespace from handler bean name.
528+
if (handler instanceof String) {
529+
handler = ((String) handler).trim();
530+
}
531+
// 这里调用的是父类的方法
532+
registerHandler(url, handler);
533+
}
534+
}
535+
}
536+
}
537+
```
538+
这个SimpleUrlHandlerMapping注册过程的完成,很大一部分需要它的基类来配合,这个基类就是AbstractUrlHandlerMapping。在AbstractUrlHandlerMapping的处理过程中,如果使用Bean的名称作为映射,那么直接从容器中获取这个HTTP映射对应的Bean,然后还要对不同的URL配置进行解析处理,比如在HTTP请求中配置成“/”和通配符“/*” 的URL,以及正常的URL请求,完成这个解析处理过程以后,会
539+
把URL和handler作为键值对放到一个handlerMap中去。
540+
```java
541+
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
542+
/**
543+
* 为给定的URL路径注册指定的handler处理程序
544+
*/
545+
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
546+
Assert.notNull(urlPaths, "URL path array must not be null");
547+
for (String urlPath : urlPaths) {
548+
registerHandler(urlPath, beanName);
549+
}
550+
}
551+
552+
/**
553+
* 为给定的URL路径注册指定的handler处理程序
554+
*/
555+
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
556+
Assert.notNull(urlPath, "URL path must not be null");
557+
Assert.notNull(handler, "Handler object must not be null");
558+
Object resolvedHandler = handler;
559+
560+
// 如果使用bean名称进行映射,就直接从IoC容器中获取该bean名称对应的handler
561+
if (!this.lazyInitHandlers && handler instanceof String) {
562+
String handlerName = (String) handler;
563+
if (getApplicationContext().isSingleton(handlerName)) {
564+
resolvedHandler = getApplicationContext().getBean(handlerName);
565+
}
566+
}
567+
568+
Object mappedHandler = this.handlerMap.get(urlPath);
569+
if (mappedHandler != null) {
570+
if (mappedHandler != resolvedHandler) {
571+
throw new IllegalStateException(
572+
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
573+
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
574+
}
575+
}
576+
else {
577+
// 处理URL是"/"的映射,把这个"/"映射的controller设置到rootHandler中
578+
if (urlPath.equals("/")) {
579+
if (logger.isInfoEnabled()) {
580+
logger.info("Root mapping to " + getHandlerDescription(handler));
581+
}
582+
setRootHandler(resolvedHandler);
583+
}
584+
// 处理URL是"/"的映射,把这个"/"映射的controller设置到defaultHandler中
585+
else if (urlPath.equals("/*")) {
586+
if (logger.isInfoEnabled()) {
587+
logger.info("Default mapping to " + getHandlerDescription(handler));
588+
}
589+
setDefaultHandler(resolvedHandler);
590+
}
591+
// 处理正常的URL映射,此handlerMap的key和value分别代表URL和映射的Controller
592+
else {
593+
this.handlerMap.put(urlPath, resolvedHandler);
594+
if (logger.isInfoEnabled()) {
595+
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
596+
}
597+
}
598+
}
599+
}
600+
601+
/**
602+
* 为此handler映射设置根handler,即要为根路径("/")注册的handler
603+
* <p>Default is {@code null}, indicating no root handler.
604+
*/
605+
public void setRootHandler(Object rootHandler) {
606+
this.rootHandler = rootHandler;
607+
}
377608

609+
public Object getRootHandler() {
610+
return this.rootHandler;
611+
}
612+
613+
/**
614+
* 设置此handler映射的默认handler。如果未找到特定映射,则将返回此handler
615+
*/
616+
public void setDefaultHandler(Object defaultHandler) {
617+
this.defaultHandler = defaultHandler;
618+
}
619+
620+
public Object getDefaultHandler() {
621+
return this.defaultHandler;
622+
}
623+
}
624+
```
625+
这里的handlerMap是一个HashMap,其中保存了URL请求和Controller的映射关系,这个handlerMap是在AbstractUrlHandlerMapping中定义的( Map<String, object> handlerMap = new LinkedHashMap<String, object>() ),这个配置好URL请求和handler映射数据的handlerMap,为Spring MVC响应HTTP请求准备好了基本的映射数据,根据这个handlerMap以及设置于其中的映射数据,可以方便地由
626+
URL请求得到它所对应的handler。有了这些准备工作,Spring MVC就可以等待HTTP请求的到来了。
627+
628+
### 4.2 使用HandlerMapping完成请求的映射处理
629+
继续通过SimpleUrlHandlerMapping的实现来分析HandlerMapping的接口方法getHandler(),该方法会根据初始化时得到的映射关系来生成DispatcherServlet需要的HandlerExecutionChain,也就是说,这个getHandler()方法是实际使用HandlerMapping完成请求的映射处理的地方。在前面的HandlerExecutionChain的执行过程中,首先在AbstractHandlerMapping中启动getHandler的调用。
630+
```java
631+
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
632+
/**
633+
* 查找给定请求的handler,如果找不到特定的handler,则返回到defaultHandler
634+
*/
635+
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
636+
// 模板方法模式
637+
Object handler = getHandlerInternal(request);
638+
// 如果找不到特定的handler,则取defaultHandler
639+
if (handler == null) {
640+
handler = getDefaultHandler();
641+
}
642+
// defaultHandler也没有则返回null
643+
if (handler == null) {
644+
return null;
645+
}
646+
// 如果该handler是String类型的,说明它是一个beanname
647+
// 根据该beanname从IoC容器中获取真正的handler对象
648+
if (handler instanceof String) {
649+
String handlerName = (String) handler;
650+
handler = getApplicationContext().getBean(handlerName);
651+
}
652+
// 这里把handler添加到到HandlerExecutionChain中
653+
return getHandlerExecutionChain(handler, request);
654+
}
655+
}
656+
```
657+
取得handler的具体过程在getHandlerInternal()方法中实现,这个方法接受HTTP请求作为参数,它的实现在AbstractHandlerMapping的子类AbstractUrlHandlerMapping中,这个实现过程包括从HTTP请求中得到URL,并根据URL到urlMapping中获得handler。
658+
```java
659+
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
660+
/**
661+
* 查找给定请求的URL路径 对应的handler
662+
*/
663+
@Override
664+
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
665+
// 从request中获取请求的URL路径
666+
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
667+
// 将得到的URL路径与handler进行匹配,得到对应的handler,如果没有对应的handler
668+
// 则返回null,这样默认的handler会被使用
669+
Object handler = lookupHandler(lookupPath, request);
670+
if (handler == null) {
671+
// We need to care for the default handler directly, since we need to
672+
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
673+
Object rawHandler = null;
674+
if ("/".equals(lookupPath)) {
675+
rawHandler = getRootHandler();
676+
}
677+
// 使用默认的handler
678+
if (rawHandler == null) {
679+
rawHandler = getDefaultHandler();
680+
}
681+
if (rawHandler != null) {
682+
// Bean name or resolved handler?
683+
if (rawHandler instanceof String) {
684+
String handlerName = (String) rawHandler;
685+
rawHandler = getApplicationContext().getBean(handlerName);
686+
}
687+
validateHandler(rawHandler, request);
688+
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
689+
}
690+
}
691+
if (handler != null && logger.isDebugEnabled()) {
692+
logger.debug("Mapping [" + lookupPath + "] to " + handler);
693+
}
694+
else if (handler == null && logger.isTraceEnabled()) {
695+
logger.trace("No handler mapping found for [" + lookupPath + "]");
696+
}
697+
return handler;
698+
}
378699

700+
/**
701+
* 查找给定URL路径的handler实例
702+
*/
703+
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
704+
// 直接匹配
705+
Object handler = this.handlerMap.get(urlPath);
706+
if (handler != null) {
707+
// Bean name or resolved handler?
708+
if (handler instanceof String) {
709+
String handlerName = (String) handler;
710+
handler = getApplicationContext().getBean(handlerName);
711+
}
712+
validateHandler(handler, request);
713+
return buildPathExposingHandler(handler, urlPath, urlPath, null);
714+
}
715+
// 正则匹配
716+
List<String> matchingPatterns = new ArrayList<String>();
717+
for (String registeredPattern : this.handlerMap.keySet()) {
718+
if (getPathMatcher().match(registeredPattern, urlPath)) {
719+
matchingPatterns.add(registeredPattern);
720+
}
721+
}
722+
String bestPatternMatch = null;
723+
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
724+
if (!matchingPatterns.isEmpty()) {
725+
Collections.sort(matchingPatterns, patternComparator);
726+
if (logger.isDebugEnabled()) {
727+
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
728+
}
729+
bestPatternMatch = matchingPatterns.get(0);
730+
}
731+
if (bestPatternMatch != null) {
732+
handler = this.handlerMap.get(bestPatternMatch);
733+
// Bean name or resolved handler?
734+
if (handler instanceof String) {
735+
String handlerName = (String) handler;
736+
handler = getApplicationContext().getBean(handlerName);
737+
}
738+
validateHandler(handler, request);
739+
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
740+
741+
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
742+
// for all of them
743+
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
744+
for (String matchingPattern : matchingPatterns) {
745+
if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
746+
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
747+
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
748+
uriTemplateVariables.putAll(decodedVars);
749+
}
750+
}
751+
if (logger.isDebugEnabled()) {
752+
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
753+
}
754+
return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
755+
}
756+
// No handler found...
757+
return null;
758+
}
759+
}
760+
```
761+
经过这一系列对HTTP请求进行解析和匹配handler的过程,得到了与请求对应的handler处理器。在返回的handler中,已经完成了在HandlerExecutionChain中进行封装的工作,为handler对HTTP请求的响应做好了准备。
379762

380763
### 4.3 DispatcherServlet对HTTP请求的分发处理
381764

382765

383766

767+
66.2 KB
Loading
40.6 KB
Loading

0 commit comments

Comments
 (0)