本人在阅读 SpringMVC
源码过程中,一直对 HandlerMapping
、HandlerAdapter
有疑惑,一直不理解。心里知道这里用的是适配器模式,本人对适配器模式还是知晓的,但这两个东西就是不理解。最近突然知道了一个知识点,瞬间豁然开朗,至于是哪个知识点,下面慢慢说。
下面这张图是SpringMVC
的工作流程图,随便一搜,应该一大把,这里只做熟悉用,不会细说。(PS:之前跳槽面试,就有一道笔试题让我画SpringMVC的工作流程。。。。)
1、请求首先进入DispatcherServlet
, 由DispatcherServlet
从HandlerMappings
中匹配对应的Handler
,此时只是获取到了对应的Handler
,然后拿着这个Handler
去寻找对应的适配器,即:HandlerAdapter
;
2、拿到对应HandlerAdapter
时,这时候开始调用对应的Handler
方法,即执行我们的Controller
来处理业务逻辑了, 执行完成之后返回一个ModeAndView
;
3、HandlerAdapter
执行完之后,返回一个ModeAndView
,把它交给我们的视图解析器ViewResolver
,通过视图名称查找出对应的视图然后返回;
4、最后,渲染视图 返回渲染后的视图。
二、SpringMVC中定义Controller的方式在介绍HandlerMapping
、HandlerAdapter
之前,先来说一下SpringMVC
中定义Handler
的方式,本人就是对这个知识点不熟悉,导致对这两个对象一直不明白。
先说一下最最最最……常用定义Handler
的方式,使用@RequestMapping
注解,下面这段代码不用介绍吧:
@Controller
public class IndexController {
@RequestMapping("/index")
@ResponseBody
public String sayHello(){
System.out.println("hello ...");
return "hello";
}
}
那大家有没有用过下面的两种方式来声明一个Handler
呢??
实现org.springframework.web.servlet.mvc.Controller
控制器接口,此接口只有一个方法handleRequest()
,用于请求的处理,返回ModelAndView
。 这个接口从第一版SpringMVC
就存在了,所以这个接口是非常古老的接口~~~也是Spring MVC
最早期的实现Handler
的方式
// 关注一下这个包
import org.springframework.web.servlet.mvc.Controller;
@Component("/home")
public class HomeController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out.println("home ...");
return null;
}
// 这地方考虑个问题:怎么样实现类似@ResponseBody的功能呢?
// 就是想实现直接向body里写数据,而不是返回一个页面。
// 如果想直接在处理器/控制器里使用response向客户端写回数据,
// 可以通过返回null来告诉 DispatcherServlet我们已经写出响应了,
// 不需要它进行视图解析。像下面这样
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out.println("home ...");
response.getWriter().write("home controller from body");
return null; // 返回null告诉视图渲染 直接把body里面的内容输出浏览器即可
}
}
实现org.springframework.web.HttpRequestHandler
接口,HttpRequestHandler
用于处理Http requests
,其类似于一个简单的Servlet
,只有一个handlerRequest()
方法,其处理逻辑随子类的实现不同而不同。
// 关注一下这个包
import org.springframework.web.HttpRequestHandler;
@Component("/login")
public class LoginController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
System.out.println("login...");
response.getWriter().write("login ...");
}
}
再来看一下servlet
的使用,是不是很相似。
@WebServlet("/myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
}
}
其实上面这两种方式第一种使用@RequestMapping
注解一样,都能定义为一个Handler
,拦截到对应的请求,并且做出响应。这地方就要牵扯出HandlerMapping
了。
从上面的分析,我们知道,Handler
的定义有上面三种(也有可能还有其他方式,比如Servlet),这地方就要引出下面这两个HandlerMapping:BeanNameUrlHandlerMapping
、RequestMappingHandlerMapping
,当然还有其他HandlerMapping
,下面的断点图也能说明这一点。
这里先说明一下,用注解@RequestMapping
定义的Handler
,用的是RequestMappingHandlerMapping
,上面的其他两种,用的是BeanNameUrlHandlerMapping
,静态资源的请求,用的是SimpleUrlHandlerMapping
。
这地方我们可以从 Spring 的角度考虑,Spring 容器在启动的时候,会去扫描所有的组件,并把它们实例化。当 Spring 容器发现一个方法用@RequestMapping
注解标注的时候,就用RequestMappingHandlerMapping
这个类去实例化,当发现一个类实现了org.springframework.web.servlet.mvc.Controller
这个接口的时候,就用BeanNameUrlHandlerMapping
去实例化,然后将所有请求放在一个Map里,用请求路径(比如:/index)和对应的Handler
做映射处理,这样是不是更好理解。
HandlerMapping的作用:主要是根据request
请求匹配/映射上能够处理当前request
的Handler
.
下面来看一下如何根据request
来获取HandlerMapping
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;
}
下面是对/index
请求的断点调试图,我们从图中可以看出,this.handlerMappings
里面有4个类,有一个为重复的。循环这个List,判断这个/index
请求是由哪个Handler
来处理(即查找HandlerMapping
的过程)。
通过循环HandlerMapping
来获取HandlerExecutionChain
,再次强调,因为spring
当中存在的Handler
有多种形式,我们处理request
需要通过HandlerExecutionChain
来反射执行Handler
当中的方法,所以不同的Handler
需要new
不同的HandlerExecutionChain
,那么问题来了HandlerExecutionChain
不知道你的Handler
是什么类型(因为HandlerExecutionChain
里只定义了一个Object handler
属性,它不知道你的Handler
是什么类型的),但是HandlerMapping
知道,所以HandlerExecutionChain
的实例化必须依赖HandlerMapping
。
好,讲到这终于明白HandlerMapping
的干嘛的了,至于如何根据/index
去找对应的Handler
和HandlerExecutionChain
,这里就不做介绍啦。 那上面几个
HandlerMapping
是怎么来的呢?Spring
容器在初始化的过程中,会调用到initStrategies
中的 initHandlerMappings(context)、initHandlerAdapters(context);
这两个方法。我们在源码包的DispatcherServlet.properties
文件下会看见, 它定义了图片里的这些属性。 第一个属性,就是我们刚看见的HandlerMappings
, 也就是说 HandlerMappings
是SpringMVC
事先定义好的,Spring容器
会帮我们创建。至于第二个属性,也就是HandlerAdapter
。 介绍完
HandlerMapping
之后,下面就要来介绍HandlerAdapter
了。
HandlerAdapter的作用:因为Spring MVC
中的Handler
可以有多种实现形式,但是Servlet
需要的处理方法的结构却是固定的,都是以request
和response
作为方法入参,那么如何让固定参数的Servlet
处理方法调用灵活的Handler
来进行处理呢?这就需要HandlerAdapter
来做适配。
为什么需要HandlerAdapter? 前面说过不同的请求会获取到不同的Handler
,那么不同的Handler
它是怎么实现处理不同的请求的呢?我的第一反应是抽象出一个接口,定义一个公共接口,然后让每个Handler
实现这个接口,我想的没问题吧,但 Spring
不是这么做的,为什么呢?
再次强调:Spring MVC
的Handler
(Controller接口,HttpRequestHandler,@RequestMapping、Servlet
)有多种表现形式,不同的Handler
,处理请求的方式是不一样的,注解@RequestMapping
方式使用的是用方法处理请求,而实现Controller
接口和HttpRequestHandler
接口方式使用的是一个类,而适配器模式就能模糊掉具体的实现,从而就能提供统一访问接口,所以这地方就要使用适配器了。
这样做的好处有两个 (1)、处理器程序,也就是Handler
,允许的是任意的Object,只要返回封装好的HandlerExecutionChain
,具体的Handler
不用管;(2)、集成第三方请求处理器的时候,本处代码也无需修改,加个适配器就行(PS:这地方可以参考文章最后的模拟程序)
HandlerMapping
的源码也说明了这一点。HandlerMapping
接口里面只有一个getHandler()
方法,而且返回类型是HandlerExecutionChain
,用HandlerExecutionChain
里面定义了一个Object
类型的handler
属性,并对handler
进行了封装,在每个请求里加入了拦截器链。然后将这个HandlerExecutionChain
里面的handler
传给了HandlerAdapter
。
这地方我们可以换个角度,就是万一处理请求的每个方法不一样怎么办?支持扩展的话,是不是就需要适配器模式了
说了这么多,是不是终于知道为什么需要HandlerAdapter
了。
在得到Handler
之后,就是下面的这行代码,我们来看一下getHandlerAdapter()
方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler)
throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
从代码中能看到,从一个this.handlerAdapters
属性里面遍历了我们的适配器。这个handlerAdapters
哪来的呢? 跟上面的this.HandlerMappings
一样,在SpringMVC
的配置文件里面配置的,也就是上图中的第二个属性。 实现
org.springframework.web.servlet.mvc.Controller
接口形式的处理器,对应的HandlerMapping
是 BeanNameUrlHandlerMapping
,对应的HandlerAdapter
是 HttpRequestHandlerAdapter
实现org.springframework.web.HttpRequestHandler
接口形式的处理器,对应的HandlerMapping
也是 BeanNameUrlHandlerMapping
,对应的HandlerAdapter
也是 HttpRequestHandlerAdapter
。
最后看一下三个适配器中的
supports()
和handle()
方法
SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter
适配org.springframework.web.servlet.mvc.Controller
这种Handler
。源码非常之简单,它是一个非常古老的适配器,几乎已弃用状态。因为它直接处理的就是源生的HttpServletRequest
和HttpServletResponse
,所以它和Servlet容器
是强绑定的。无数据自动封装、校验等一系列高级功能,所以实际应用中此种方式很少被使用。
// 适配`org.springframework.web.servlet.mvc.Controller`这种Handler
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
// 最终执行逻辑的还是Handler啊~~~~
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
}
HttpRequestHandlerAdapter
HttpRequestHandlerAdapter
适配org.springframework.web.HttpRequestHandler
这种Handler
。它比Controller
方式还源生。 它和上面的唯一不同是:return null
。那是因为HttpRequestHandler#handleRequest()
它没有返回值,这就需要全靠开发者自己写response
,而Controller
最起码来说还有Model和View
自动渲染的能力。
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
RequestMappingHandlerAdapter
RequestMappingHandlerAdapter
主要是支持到了org.springframework.web.method.HandlerMethod
这种handler
,显然这种处理器也是我们最最最最为常用的,它已经把HandlerMethod
的实现精确到了使用@RequestMapping
注解标注的方法。这个类,我们要查看它的父类AbstractHandlerMethodAdapter
。
public class AbstractHandlerMethodAdapter {
// 只处理HandlerMethod 类型的处理器。抽象方法supportsInternal默认返回true
// 是留出的钩子可以给你自己扩展的
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod
&& supportsInternal((HandlerMethod) handler));
}
@Override
public final ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
// 抽象方法交给子类handleInternal去实现
return handleInternal(request, response, (HandlerMethod) handler);
}
}
看完之后,再来读一下DispatcherServlet#doDispatch()
方法的分发流程,看看DispatcherServlet
是如何使用HandlerMapping和HandlerAdapter
。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
throws Exception {
...
//1、根据URL(当然不一定非得是URL)匹配到一个处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 若匹配不到Handler处理器,就404了
noHandlerFound(processedRequest, response);
return;
}
//2、从HandlerExecutionChain里拿出Handler(注意是Object类型哦~ )然后找到属于它的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
//3、执行作用在此Handler上的所有拦截器的Pre方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//4、真正执行handle方法(也就是你自己书写的逻辑方法),得到一个ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//5、视图渲染
applyDefaultViewName(processedRequest, mv);
//6、执行拦截器的post方法(可见它是视图渲染完成了才会执行的哦~)
mappedHandler.applyPostHandle(processedRequest, response, mv);
...
//7、执行拦截器的afterCompletion方法(不管抛出与否)
}
从执行步骤中可以看到:HandlerAdapter
对于执行流程的通用性起到了非常重要的作用,它能把任何一个Handler(注意是Object类型)
都适配成一个HandlerAdapter
,从而可以做统一的流程处理,这也是为何DispatcherServlet
它能作为其它web处理框架
的分发器的原因,因为它没有耦合具体的处理器,你完全可以自己去实现。
如果上面的讲法,你还是不懂,下面就用适配器模式模拟一下,这两个类的具体调用情况,应该会一目了然。下面是依赖关系的类图。其中Controller
代表的就是 HandlerMapping
。具体代码,可以在文章最后下载。
//多种Controller实现
public interface Controller {
}
// 注意这里每个实现,都用了不同的方法名, 如果都用一样的话,就可以放到接口中了
class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("http...");
}
}
class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("simple...");
}
}
class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("annotation...");
}
}
// 定义一个Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
// 多种适配器类
class SimpleHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
class HttpHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
class AnnotationHandlerAdapter implements HandlerAdapter {
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
public class DispatchServlet {
public static List handlerAdapters = new ArrayList();
public DispatchServlet() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch() {
// 此处模拟SpringMVC从request取handler的对象,
// 适配器可以获取到希望的Controller
HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
//SimpleController controller = new SimpleController();
// 得到对应适配器
HandlerAdapter adapter = getHandler(controller);
// 通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
//遍历:根据得到的controller(handler), 返回对应适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}
注意:Controller
接口的每个实现类,都用了不同的方法名, 这样的话就需要用到适配器模式了,如果都用一样的话,就可以放到接口中了,这样是不是可以理解SpringMVC
中此处的HandlerAdapter
了
还是做个小结吧。 SpringMVC
的Handler
有多种实现方式(Controller,HttpRequestHandler,Servlet等),例如继承Controller
接口的形式,基于注解@Controlle
r控制器方式的,HttpRequestHandler
方式的。由于实现方式不一样,调用方式就不确定。
继承 Controller 方式所使用的HandlerMapping
:BeanNameUrlHandlerMapping
, 继承 Controller 方式所使用的适配器:SimpleControllerHandlerAdapter
、 注解方式@Controller
的HandlerMapping
器:RequestMappingHandlerMapping
注解方式@Controller
适配器:RequestMappingHandlerAdapter
、
这是一一对应的。
如果说的不对的,反馈一下给我啊,谢谢……
代码下载:
https://github.com/Hofanking/spring-boot-demo