本人在阅读 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接口的形式,基于注解@Controller控制器方式的,HttpRequestHandler方式的。由于实现方式不一样,调用方式就不确定。
继承 Controller 方式所使用的HandlerMapping:BeanNameUrlHandlerMapping, 继承 Controller 方式所使用的适配器:SimpleControllerHandlerAdapter 、 注解方式@Controller的HandlerMapping器:RequestMappingHandlerMapping 注解方式@Controller适配器:RequestMappingHandlerAdapter、
这是一一对应的。
如果说的不对的,反馈一下给我啊,谢谢……
代码下载:
https://github.com/Hofanking/spring-boot-demo
