SpringMVC中的Handler、HandlerMapping、HandlerAdapter到底是啥
这东西,虽然说和我们的开发没啥关系,尤其是当你用SpringBoot进行开发时,这些接口离你越来越远了。讲实话,要不是这学期扫一眼学校的课件,我都不知道有这东西,这东西本来就是对使用框架进行开发的开发者隐藏的。人家好不容易隐藏起来,你却要我们学起来,没事儿干了吧。
下图是网上流传的总览图,来自这篇文章:SpringMVC框架理解
下面通过阅读源码,来学习这些接口都是干啥的
DispathcerServlet
不管是哪个Web框架,基于什么语言,都会提供一个在整个系统最前端接受用户请求的东西,我们暂且称它“前端调度器”,它会解析用户请求请求,调度你编写的用于接收请求的组件。这样,你可以根据不同的请求编写不同的组件,在SpringMVC里,DispathcerServlet
就是前端调度器,Controller
就是你编写的处理请求的组件。
SpringMVC也是基于JavaWeb的那套ServletAPI的,所以,它使用一个Servlet
用来接收所有请求,它就像一个桥,一头是ServletAPI,一头是SpringMVC,把Servlet世界里的话翻译成框架中的通用语言。
既然是Servlet,那我们就看它的doService
方法呗:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// ... 省略一些代码 ...
try {
doDispatch(request, response);
}
// ... 再省略一些代码 ...
}
调用了doDispatch
来执行调度。doDispatch
里的代码太多了,我做了精简之后还是很多,所以我在代码中写上注释:
@SuppressWarnings("deprecation")
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// 记住它的类型,是一个Handler执行链
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
Exception dispatchException = null;
// ... 省略代码 ...
try {
// processedRequest是经过一定处理的请求对象
// 这里是根据请求,获取一个能够处理该请求的Handler对象(实际是一个Handler执行链)
// Handler用于对请求进行处理
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据Handler对象获取一个HandlerAdapter对象
// 具体为啥要这一层,我们稍后会说
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 在实际执行对请求的处理之前,先调用Handler的预处理方法`preHandle`
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用Handler,这里调用的是Handler的适配器对象,HandlerAdapter,它会返回一个ModelAndView
mv = ha.handle(processedRequest, response,
// 请求处理完毕,这里调用Handler的`postHandle`方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
// 记录异常
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 对上面一同折腾得到的处理结果进行处理,返回给前端
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
目前来看,DispatcherServlet
对一个请求调度的过程是:
- 根据请求获取能够处理的
Handler
对象 - 根据
Handler
对象获取它的适配器对象HandlerAdapter
- 调用
Handler
的preHandle
,进行请求预处理 - 调用
HandlerAdapter
的handle
,实际处理请求,返回ModelAndView
- 调用
Handler
的postHandle
,善后 - 上面的过程中如果出现异常,记录
- 最后,根据请求对象,响应对象,
Handler
对象,ModelAndView
对象,异常对象的状态,向前端返回一个结果
从这些流程里,我们能够知道,Handler
才是实际处理请求的对象,而且根据它的类型HandlerExecutionChain
,我们能猜测,实际处理请求的可能不止是一个处理器,而是一串处理器。HandlerAdapter
是对该对象的再次包装,而且使用适配器模式,应该是为了兼容某些不兼容的接口。
下面我们继续深入
getHandler如何实现
下面是getHandler
的代码,我们可以看出,它遍历了类中的一个叫handlerMappings
的可迭代对象,这证明,DispathcerServlet
维护了一系列用于映射请求到Handler
的HandlerMapping
对象。
而且,getHandler
的任务就是从这些HandlerMapping
中找到一个能处理当前请求的并返回,否则返回null。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
HandlerMapping是啥?
public interface HandlerMapping {
// ... 省略掉一些无关方法 ...
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
getHandler
方法返回一个用于参数中的请求对象的Handler和任何的Interceptors,选择的依据可能是请求URL、session状态或任何实现类选择的其它因素。
返回的HandlerExecutionChain
包含一个Object类型的handler对象,而不是任何一种接口类型,这是为了处理器可以不受任何约束。举个例子,一个HandlerAdapter
可以被写成允许使用其它框架的Handler
对象。
HandlerExecutionChain如何实现
通过上面的分析,我们已经知道了HandlerExecutionChain
中包含了实际的Handler对象,它用于处理请求,这个对象不与任何框架的处理器对象耦合,然后还包含了一系列用于该请求的HandlerInterceptor
。
private final Object handler;
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
我们知道SpringMVC的HandlerInterceptor
对象用于拦截一个请求,它可以在一个请求执行前后做一些处理。这是我们经常使用的类。不知道也没关系,现在你知道它的作用了。
我们可以看到,HandlerExecutionChain
提供了一些方法,applyPreHandle
用于调用所有interceptor的preHandle
方法,如果该方法返回false
,证明该Interceptor不希望请求继续传递给链中的下一个处理器,该请求应该被拦截,到此为止。所以它判断了返回值,并且当返回false时停止执行。其它的applyPostHandle
、triggerAfterCompletion
也都大同小异。这是职责链设计模式。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception { /* ... */ }
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) { /* ... */ }
回到doDispatch
现在,doDispatch
中下面的高亮部分的代码,是不是不用注释也能懂了:
+mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
+if (!mappedHandler.applyPreHandle(processedRequest, response)) {
+ return;
+}
mv = ha.handle(processedRequest, response,
+mappedHandler.applyPostHandle(processedRequest, response, mv);
目前,我们知道了HandlerMapping
是用于将请求映射到一个HandlerExecutionChain
的,而HandlerExecutionChain
主要执行该请求的所有Interceptor,用于对请求进行拦截和善后,并且它保存了实际的Handler
对象,该对象是Object类型,不和任何框架中的处理器类耦合,这给了其它框架整合到SpringMVC的能力。
HandlerAdapter
现在我们的版图只剩下HandlerAdapter
一块了,我虽文思泉涌,但奈何我妈喊我吃饭。看到这,不妨你也休息一下。
既然SpringMVC选择了Object
类型的Handler
对象来兼容其它框架,那么必定要有一个适配器对象来将这个Handler
适配到SpringMVC中,如果不经过任何处理,就指望着其它框架中的处理器与SpringMVC中的接口完全兼容是不可能的。所以这里要用到适配器模式。
getHandlerAdapter方法
也不难看出,和HandlerMapping
一样,DispatcherServlet
中也维护了HandlerAdapter
的一个可迭代结构——handlerAdapters
。
getHandlerAdapter
方法遍历这个适配器链,调用它们的supports
方法,传入Object类型的handler,看看适配器是否适配这个处理器。它找到第一个适配这个处理器的适配器并返回,否则就抛出异常。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
HandlerAdapter长什么样
HandlerAdapter
接口很简单,supports
方法用于判断传入的Handler是否被当前适配器所适配,handle
方法用于实际的处理请求,它一边调用自己的Handler
对象的API,一边尽力去适配SpringMVC的API,比如返回Spring中的ModelAndView
。它是任何Handler(兼容Spring或不兼容Spring的)与Spring框架间的桥梁。
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// ... 省略一个已废弃方法 ...
}
support方法
比如你有一个自己开发的框架——FlyMVC
,你的框架有自己的处理器接口FlyHandler
,该处理器通过HandlerMapping被添加到HandlerExecutionChain中。但它并不兼容Spring框架,为了让它与Spring协同工作,你需要编写一个HandlerAdapter
,并且,它的supports
方法可能如下:
public boolean supports(Object handler) {
return handler instanceof FlyHandler;
}
SpringMVC中,谁扮演Handler?
毫无疑问是Controller
。
如果你在新版Idea中,在HandlerAdapter
类上会看到这个,你点一下就可以看到所有实现类:
点开我们就能看到有一个SimpleControllerHandlerAdapter
,它肯定是用于SpringMVC的Controller的HandlerAdapter
。它的实现简单至极:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
// ... 省略一个无关方法 ...
}
如果你实现了Controller
接口来编写一个请求处理器,那么这个HandlerAdapter
会被应用。同时,因为Controller
本就是SpringMVC中的组件,它的handleRequest
方法返回的本就是ModelAndView
,所以,它的handle
方法很简单,并看不出做了什么不兼容接口之间的转换,毕竟都是SpringMVC里的组件。
没实现Controller
接口的Controller
怎么处理的?
我们知道,在Spring中编写Controller
并不非要实现接口,而且Spring官方宣扬的最小侵入性也鼓励我们尽量不要用接口的方式,这会让我们编写的组件和Spring框架耦合。
我们可以这样编写一个Controller
:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
这个Controller并没有实现任何接口,它是怎么被DispatcherServlet
调度的?这里,我盲猜是Spring会将它转换成某个Controller
接口的实现类,然后继续按照上面的流程,使用SimpleControllerHandlerAdapter
来调度它。
我们在这里打上断点,然后运行程序,访问/hello
:
断点没有任何反应,请求直接通过,这说明不实现接口来编写的Controller
并不是按我们之前想的那样工作的,完全是两个流程,根本没用到SimpleControllerHandlerAdapter
。
下面我们把断点打到DispatcherServlet
中:
这次我们看到,handler
是一个HandlerMethod
对象的实例,它的HandlerAdapter
是RequestMappingHandlerAdapter
:
HandlerMethod
基于非接口方式编写的Controller
,每一个方法处理一个请求,所以,HandlerMethod
代表的就是一个请求方法。
RequestMappingHandlerAdapter
它用于处理标注了@RequestMapping
的HandlerMethod
对象,因此得名。
它扩展自AbstractHandlerMethodAdapter
,supports
方法也是在父类中完成的:
// AbstractHandlerMethodAdapter.supports()
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
该方法只判断了handler
是否是HandlerMethod
,而且,它考虑到它的子类有可能在能够处理的方法上有其它限制,所以它还调用了子类的supportsInternal
方法,子类可以通过该方法来判断是否支持处理对应的HandlerMethod
。
AbstractHandlerMethodAdapter
中的handle
方法也极其简单,直接调用子类的handleInternal
方法,这属于模板设计模式:
// AbstractHandlerMethodAdapter.handle()
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
下面来看看RequestMappingHandlerAdatper.handleInternal
怎么写的:
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// ... 省略 ...
return mav;
}
有点长,总结起来就是根据不同情况,做不同的处理,再使用invokeHandlerMethod
来调用具体的HandlerMethod
,最后返回ModelAndView
。
为什么这里不直接调用HandlerMethod
,而是包装了一层方法呢?要知道,你编写的那些Controller方法可不一定返回ModelAndView
,将你那些方法返回的东西按情况合理的转换成ModelAndView
,就是该invokeHandlerMethod
方法存在的理由。
具体的代码我就不看了,我觉得到这就足够满足我的好奇心了。
总结
看下面这张图,自己总结一遍,注意,从视图解析器部分开始,都不是本篇文章讨论的范围。
我的总结
DispatcherServlet
接收前端请求,找到一个用于处理该请求的HandlerMapping
HandlerMapping
返回一个HandlerExecutionChain
,它其中包含了一堆Interceptor和一个任意类型的Handler对象- 执行Interceptor的preHandle方法
- 为了将任意类型的Handler适配到SpringMVC,所以需要为每种Handler提供一个HandlerAdapter
- 执行所有HandlerAdapter的supports方法,找到支持该种Handler的适配器
- 执行找到的HandlerAdapter的handle方法,处理请求,返回ModelAndView
- 执行Interceptor的postHandle方法
- 如果一切正常,结束,返回
- 如果发生异常,记录异常,返回