ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringMVC中的Handler、HandlerMapping、HandlerAdapter到底是啥

2022-07-03 10:34:55  阅读:135  来源: 互联网

标签:HandlerMapping 请求 SpringMVC handler request HandlerAdapter Handler response


这东西,虽然说和我们的开发没啥关系,尤其是当你用SpringBoot进行开发时,这些接口离你越来越远了。讲实话,要不是这学期扫一眼学校的课件,我都不知道有这东西,这东西本来就是对使用框架进行开发的开发者隐藏的。人家好不容易隐藏起来,你却要我们学起来,没事儿干了吧。

下图是网上流传的总览图,来自这篇文章:SpringMVC框架理解

img

下面通过阅读源码,来学习这些接口都是干啥的

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对一个请求调度的过程是:

  1. 根据请求获取能够处理的Handler对象
  2. 根据Handler对象获取它的适配器对象HandlerAdapter
  3. 调用HandlerpreHandle,进行请求预处理
  4. 调用HandlerAdapterhandle,实际处理请求,返回ModelAndView
  5. 调用HandlerpostHandle,善后
  6. 上面的过程中如果出现异常,记录
  7. 最后,根据请求对象,响应对象,Handler对象,ModelAndView对象,异常对象的状态,向前端返回一个结果

从这些流程里,我们能够知道,Handler才是实际处理请求的对象,而且根据它的类型HandlerExecutionChain,我们能猜测,实际处理请求的可能不止是一个处理器,而是一串处理器。HandlerAdapter是对该对象的再次包装,而且使用适配器模式,应该是为了兼容某些不兼容的接口

下面我们继续深入

getHandler如何实现

下面是getHandler的代码,我们可以看出,它遍历了类中的一个叫handlerMappings的可迭代对象,这证明,DispathcerServlet维护了一系列用于映射请求到HandlerHandlerMapping对象。

而且,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时停止执行。其它的applyPostHandletriggerAfterCompletion也都大同小异。这是职责链设计模式。

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类上会看到这个,你点一下就可以看到所有实现类:

img

点开我们就能看到有一个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

img

断点没有任何反应,请求直接通过,这说明不实现接口来编写的Controller并不是按我们之前想的那样工作的,完全是两个流程,根本没用到SimpleControllerHandlerAdapter

下面我们把断点打到DispatcherServlet中:

img

这次我们看到,handler是一个HandlerMethod对象的实例,它的HandlerAdapterRequestMappingHandlerAdapter

img

HandlerMethod

基于非接口方式编写的Controller,每一个方法处理一个请求,所以,HandlerMethod代表的就是一个请求方法。

RequestMappingHandlerAdapter

它用于处理标注了@RequestMappingHandlerMethod对象,因此得名。

它扩展自AbstractHandlerMethodAdaptersupports方法也是在父类中完成的:

// 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方法存在的理由。

具体的代码我就不看了,我觉得到这就足够满足我的好奇心了。

总结

看下面这张图,自己总结一遍,注意,从视图解析器部分开始,都不是本篇文章讨论的范围。

img

我的总结

  1. DispatcherServlet接收前端请求,找到一个用于处理该请求的HandlerMapping
  2. HandlerMapping返回一个HandlerExecutionChain,它其中包含了一堆Interceptor和一个任意类型的Handler对象
  3. 执行Interceptor的preHandle方法
  4. 为了将任意类型的Handler适配到SpringMVC,所以需要为每种Handler提供一个HandlerAdapter
  5. 执行所有HandlerAdapter的supports方法,找到支持该种Handler的适配器
  6. 执行找到的HandlerAdapter的handle方法,处理请求,返回ModelAndView
  7. 执行Interceptor的postHandle方法
  8. 如果一切正常,结束,返回
  9. 如果发生异常,记录异常,返回

标签:HandlerMapping,请求,SpringMVC,handler,request,HandlerAdapter,Handler,response
来源: https://www.cnblogs.com/lilpig/p/16439356.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有