本系列文章皆在分析SpringMVC
的核心组件和工作原理,让你从SpringMVC
浩如烟海的代码中跳出来,以一种全局的视角来重新审视SpringMVC
的工作原理.
思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜
前言
在SpringMVC流程分析(四):SpringMVC中如何为一个请求选择合适的处理器 中,我们通过对doDispatch
方法中getHandler
方法分析由浅入深的讨论了HandlerMapping
组件的相关内容。通过分析我们知道了HanlderMapping
的主要工作就是根据前端传来的请求,然后找到合适的处理器。
此外,在分析HanlderMapping
的过程中我们注意到在getHandler
方法的内部会遍历全部的HanlderMapping
信息,然后逐一进行匹配。如果找到合适的处理器组件,则返回一个HandlerExecutionChain
对象。
而在上一节SpringMVC流程分析(五):HandlerInterceptor组件------SpingMVC中优雅的对请求中,我们指出HandlerExecutionChain
其实相当于对处理器的一种封装,其内部会包含处理器handler
和拦截器HandlerInterceptor
。其中。处理器
很好理解,无非就是在程序中被我们使用@GetMapping,@PostMapping
注解所标记的方法。
进一步,通过上述doDispatch
的调用链我们注意到,当通过getHanlder
方法获取到一个HandlerExecutionChain
对象后,其后续会执行getHandlerAdapter
方法并获取一个HandlerAdapter
对象。 这个HandlerAdapter
又是什么呢?别急,本章我们便来揭开HandlerAdapter
神秘面纱。
(注:本章我们主要探究getHandlerAdapter(mappedHandler.getHandler())
和ha.handle
两个方法的执行逻辑。)
下图展示了本系列文章重点分析的组件信息,其中 HandlerAdapter
为本文分析的重点。
getHandlerAdapter
的相关逻辑
首先来分析getHandlerAdapter
的相关逻辑。
java
protected HandlerAdapter getHandlerAdapter(Object handler)
throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
// 省略异常处理
}
可以注意到,getHandlerAdapter
的处理逻辑为遍历容器中的全部的HandlerAdapter
,并找到一个可以处理当前处理器的HandlerAdapter
返回。
如果你看过之前我们对于HanlderMapping
的分析,你会发现,此处的逻辑同getHandler
方法的逻辑十分类似。在SpringMVC流程分析(四):SpringMVC中如何为一个请求选择合适的处理器 中分析HandlerMapping
时,我们提到getHandler
的逻辑为:"遍历容器所有的HandlerMapping
信息,寻找到一个可以处理当前请求的Handler
,并将其构建为HandlerExecutionChain
的一个对象。"
如何对处理器进行封装
java
public void doDipatch(HttpServletRequest request, HttpServletResponse response) {
// ... 省略其他无关代码
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ... 省略其他无关代码
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
我们注意到,doDispatch
调用getHandlerAdapter
时,会首先调用mappedHandler.getHandler()
,而其作用在于获取到HandlerExecutionChain
中的处理器信息。换言之,对于getHandlerAdapter
而言,其需要将一个处理器
作为入参。此时我们不免会有这样的疑惑,我们知道所谓的处理器
本质就是处理器某请求的逻辑信息,既然HandlerExecutionChain
已经持有处理器了,为什么不直接调用,反而又要根据处理器
获取到一个HandlerAdapter
信息,然后再将处理器
传入到HandlerAdapter
的handler
方法呢?
此时你可能会想,这里使用HandlerAdapter
是不是多此一举呢?我明明直接获取HandlerExecutionChain
中的处理器
,然后执行相关逻辑就可以了,为什么又要使用这个HandlerAdapter
呢?
那这个HandlerAdapter
是多余的吗?当然不是! 你之所以认为只要调用处理器
就可以,完全是基于这样一种假设,即处理器
是一个方法,这个方法内部定义了处理请求的逻辑信息,所以你会简单的认为执行处理逻辑本质就是调用方法。
但是别忘了,在SpringMVC
中不仅标有@RequestMapping
的方法会被认为是处理器
,同时实现Controller
接口,重写其中的handlerRequest
的方法也能在SpringMVC
中定义一个处理器。此外,还可以实现HttpRequestHandler
接口,重写其中的handleRequest
方法也可以实现。
综上所示,在SpringMVC
中定义处理的方法主要有两种方式:
- 使用
@RequestMapping
标注的方法 - 实现
Controller
或HttpRequestHandler
接口,并重写其中的handleRequest
方法
针对上述两种情况,SpringMVC
框架的开发者所面临问题就是,针对处理器
的不同实现方式,究竟该如何处理才能处理器
的逻辑正常执行?
答案就是"适配"。但不同处理器
在实现方式还是有所差异的,例如,基于接口Controller
和注解@RequestMapping
修饰的方法就是两种不同的实现方式。 那在适配时该如何处理呢?很简单,只需提供一个判断方法即可,用以判断处理器
的实现方式,从而为其选择合适的"适配器"。 进一步,当完成适配后,我们需要仅仅是调用其中的方法,就能完成逻辑的执行。
事实上,这就是HandlerAdapter
为我们做的事情。明白了这些,相信接下来再看HandlerAdapter
的相关分析一定会一种拨云见日的感觉。
深入理解HandlerAdapter
通过之前的分析,我们可以知道HandlerAdapter
的作用是将不同类型的处理器进行适配,从而实现统一的请求处理流程。
HandlerAdapter
的体系结构
其中:
RequestMappingHandlerAdapter
:这是Spring MVC
默认使用的HandlerAdapter
,用于处理带有@RequestMapping
注解的Controller
方法。它支持将请求参数绑定到方法参数、处理返回值并选择视图解析器等。HttpRequestHandlerAdapter
:用于处理实现了HttpRequestHandler
接口的Controller
,这些Controller
直接操作HttpServletRequest
和HttpServletResponse
对象,通常用于返回原始的响应数据,如文件下载等。SimpleControllerHandlerAdapter
:用于处理实现了Controller
接口的Controller
,这种Controller
需要实现handleRequest
方法来处理请求。
此外,这些HandlerAdapter
实现都遵循了统一的接口和抽象,使得 Spring MVC
可以根据请求的类型选择合适的 HandlerAdapter
来进行处理。接口HandlerAdapter
定义的内容如下:
java
public interface HandlerAdapter {
/**
* 判断能否适配当前处理器
* */
boolean supports(Object handler);
/**
* 执行处理器的方法
* */
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception;
}
可以看到HandlerAdapter
内部方法的作用如下:
support
判断当前当前适配器能否适配该处理器hanlder
执行处理中的处理逻辑
当我们知晓了HandlerAdapter
的类结构关系和内部定义的方法后,再回看之前getHandlerAdapter
中的代码是不是感觉getHandlerAdapter
逻辑十分简单?
让我们把视角再拉回到getHandlerAdapter
中,我们注意到,既然getHandlerAdapter
内部会遍历全部的HandlerAdapter
组件,并通过其Support
方法为处理器
选择合适的适配器,但我们使用SpringMVC
时好像并没有显示的配置过有关适配器的相关信息,那这个适配器是在哪加载的呢?
别着急,接下来我们便来分析在SpringMVC
内部在加载时默认会为我们加载哪些适配器。
HandlerAdapter
初始化的相关逻辑
在SpringMVC流程分析(四):SpringMVC中如何为一个请求选择合适的处理器 中有提到过,"SpringMVC
框架在使用过程中,通常会遵守一种约定优于配置的原则,即通过一系列约定和默认配置来减少开发人员需要手动进行配置的工作,从而提高开发效率和降低代码复杂性"
而HandlerMapping
组件的初始化的相关逻辑在 DispatcherServlet
的initHandlerAdapters(ApplicationContext context)
方法中进行了定义。 而该方法会在 onRefresh
方法被调用,初始化 HandlerAdapter
组件。initHandlerAdapters
方法的逻辑如下:
java
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.handlerAdapters == null) {
// 调用初始策略
this.handlerAdapters = getDefaultStrategies(context,
HandlerAdapter.class);
// 省略其他无关代码.....
}
}
上述代码逻辑很简单,如果当前容器中没有找到相关的HandlerAdapter
,则会通过方法 getDefaultStrategies
加载默认的适配信息。
而在SpringMVC
中,此处定义的 getDefaultStrategies
逻辑,会默认从配置文件中加载如下三个HandlerAdapter
信息。
ini
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.
mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
即默认会加载HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter
。
至此,我们以doDispatch
中的getHanlderAdapter
方法为起点,分析发现其内部逻辑为遍历容器
内部的HandlerAdapter
信息。进一步,以此为基础我们分析HandlerAdapter
的类结构关系和功能方法。更进一步,我们分析讨论了SpringMVC
内部HandlerAdapter
的初始过程。
接下来,我们便开始着重分析doDispathcer
中的另外一个方法ha.handler()
的相关内容。事实上,ha.hanlder()
背后的逻辑就是调用HandlerAdapter
的handler
方法。
适配器中hanlder的处理逻辑
对于适配器HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter
来说,其主要用于处理实现Controller
或HttpRequestHandler
接口的处理器,其中的handler
方法的逻辑相对简单,在此便不加叙述,我们重点关注RequestMappingHandlerAdapter
中handler
方法的相关逻辑。
抽象基类:AbstractHandlerMethodAdapter
通过之前HandlerAdapter
的结构关系可以知道,对于RequestMappingHandlerAdapter
而言,其会继承自AbstractHandlerMethodAdapter
。而AbstractHandlerMethodAdapter
的逻辑如下:
java
public abstract class AbstractHandlerMethodAdapter
extends WebContentGenerator
implements HandlerAdapter, Ordered {
// .......省略其他无关代码
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception {
return handleInternal(request, response,
(HandlerMethod) handler);
}
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception;
// .......省略其他无关代码
}
可以看到AbstractHandlerMethodAdapter
中的hanlder
的处理逻辑会委托给子类的handleInternal
进行实现,所以如果我们要讨论RequestMappingHandlerAdapter
中对于处理器
的操作逻辑,那我们只需关注其中的handleInternal
即可。
(注:AbstractHandlerMethodAdapter
的逻辑其实很简单,其将一部分执行逻辑都交给了子类处理,但如果不看AbstractHandlerMethodAdapter
上来直接分析RequestMappingHandlerAdapter
中的handleInternal
方法,会很跳跃,不利于理解。)
内部核心成员变量
RequestMappingHandlerAdapter
是 HandlerAdapter
接口的实现类,用于适配处理带有 @RequestMapping
注解的方法,同时处理参数解析、方法调用和返回值处理等操作。 其内部关键成员信息如下所示:
其中:
HandlerMethodArgumentResolverComposite argumentResolvers
:参数处理器组合对象HandlerMethodReturnValueHandlerComposite returnValueHandlers
:返回值处理器组合对象List<HttpMessageConverter<?>> messageConverters
:HTTP
消息转换器集合对象List<Object> requestResponseBodyAdvice
:RequestResponseAdvice
集合对象
由于被@RequestMapping
注解标注的方法会被认为是处理器
,所以执行处理器
的本质逻辑就在于方法的执行。而从一个http
请求进入到SpringMVC
开始,直到为http
请求找到对应处理器
的这一过程中,处理器
所需的参数信息仍在http
请求中,其并未得到解析,所以如果要保证处理器
可以执行,则需要面临参数解析提供方法
所需参数,返回值处理解决返回值类型映射等问题。所以RequestMappingHandlerAdapter
内部才会有上图所示的成员信息。
进一步,由于RequestMappingHandlerAdapter
会实现InitializingBean
接口,因此上述成员信息的初始化操作全部放到了方法afterPropertiesSet
中进行初始化。相关执行流程图如下所示:
执行HanlderMapping
的处理"方法"
如之前分析中我们提到,在基类AbstractHandlerMethodAdapter
中会将hanlder
的处理方法的相关的逻辑委托于子类的handleInternal
进行实现。接下来,我们看看RequestMappingHandlerAdapter
中的handleInternal
究竟做了哪些工作。
RequestMappingHandlerAdapter
#handleInternal
java
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// .... 省略其他无关代码
// <2> 调用 HandlerMethod 方法
mav = invokeHandlerMethod(request, response, handlerMethod);
// .... 省略其他无关代码
return mav;
}
可以看到,对于RequestMappingHandlerAdapter
中的handleInternal
而言,其关键逻辑在于invokeHandlerMethod
,从方法名字就可以看出,其主要任务肯定是执行 HandlerMethod
。
此时,你可能会疑惑,我知道HandlerMapping、HandlerHandlerAdapter
,那这个HandlerMethod
又是什么呢?
简单来说,在SpringMVC
中被@RequestMapping
标注的方法会被解析为一个HandlerMethod
对象。而HandlerMethod
是一个用于封装处理http
请求的方法及其相关信息的类。
进一步,当一个http
请求到达时,Spring MVC
会根据请求的URL
路径和HTTP
方法,查找匹配的HandlerMethod
,然后将请求参数解析并传递给该方法。HandlerMethod
对象包含了以下重要的信息:
- 控制器对象:指定了处理请求的控制器实例。
- 处理方法:指定了要执行的具体处理方法。
- 方法参数:包含了方法的参数信息,这些参数会从
HTTP
请求中提取而来。 - 返回值类型:定义了处理方法的返回值类型。
总结来看,HandlerMethod
对象的作用是使得Spring MVC
能够在请求到达时,动态地找到匹配的处理方法并进行调用其中的处理方法。
看到此,相信你一定会有一种恍然大悟的感觉,这所谓的handler
背后的逻辑无非就是调用被@ReqeustMapping
标注的方法
。同时,在处理过程中还会依赖一些额外的组件信息来完成参数解析,返回值处理等额外的操作。
至于如何执行被@ReqeustMapping
标注的方法
的方法,答案其实已经很明显了,其背后的逻辑肯定是依赖java
中的反射机制。
总结
Spring MVC 通过 HandlerMapping
组件会为请求找到合适的 HandlerExecutionChain
处理器执行链,包含处理器( handler
)和拦截器( interceptors
**)。
在之前SpringMVC流程分析(四):SpringMVC中如何为一个请求选择合适的处理器一文中我们有提到处理器的实现有多种,例如,通过实现 Controller
接口、HttpRequestHandler
接口,或者使用@RequestMapping
注解将方法作为一个处理器等。
这就导致 Spring MVC
无法直接执行这个处理器,所以这里需要一个处理器适配器,由它去执行处理器。而HandlerAdapter
处理器适配器对应的也有多种,哪种适配器支持处理这种类型的处理器,则由该适配器去执行,如下:
HttpRequestHandlerAdapter
:执行实现了 HttpRequestHandler 接口的处理器SimpleControllerHandlerAdapter
:执行实现了 Controller 接口的处理器SimpleServletHandlerAdapter
:执行实现了 Servlet 接口的处理器RequestMappingHandlerAdapter
:执行 HandlerMethod 类型的处理器,也就是通过@RequestMapping
等注解标注的方法
本文重点分析了 RequestMappingHandlerAdapter 对象,因为这种方式是目前使用最普遍的,其他类型的 HandlerAdapter
处理器适配器做了解即可