1. 概述
本文,我们来分享 HandlerMapping 组件。在 《精尽 Spring MVC 源码分析 —— 组件一览》 中,我们对它已经做了介绍:
org.springframework.web.servlet.HandlerAdapter
,处理器适配器接口。代码如下:
// HandlerAdapter.java |
- 因为,处理器
handler
的类型是 Object 类型,需要有一个调用者来实现handler
是怎么被使用,怎么被执行。而 HandlerAdapter 的用途就在于此。可能如果接口名改成 HandlerInvoker ,笔者觉得会更好理解。
2. HandlerAdapter
HandlerAdapter 的子类比较多,整体类图如下:HandlerAdapter 类图
- 左边的 AbstractHandlerMethodAdapter 和 RequestMappingHandlerAdapter 相对复杂
- 右边的 SimpleServletHandlerAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter 相对简单
那么,我们从难的简单的开始。哈哈哈哈。
3. SimpleControllerHandlerAdapter
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
,实现 HandlerAdapter 接口,基于 org.springframework.web.servlet.mvc.Controller
的 HandlerAdapter 实现类。代码如下:
// SimpleControllerHandlerAdapter.java |
<1>
处,判断处理器handler
是 Controller 类型。注意,不是@Controller
注解。<2>
处,调用Controller#handleRequest(HttpServletRequest request, HttpServletResponse response)
方法,Controller 类型的调用。
4. HttpRequestHandlerAdapter
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
,实现 HandlerAdapter 接口,基于 org.springframework.web.HttpRequestHandler
的 HandlerAdapter 实现类。代码如下:
// HttpRequestHandlerAdapter.java |
5. SimpleServletHandlerAdapter
org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
,实现 HandlerAdapter 接口,基于 javax.servlet.Servlet
的 HandlerAdapter 实现类。代码如下:
// SimpleServletHandlerAdapter.java |
6. AbstractHandlerMethodAdapter
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter
,实现 HandlerAdapter、Ordered 接口,继承 WebContentGenerator 抽象类,基于 org.springframework.web.method.HandlerMethod
的 HandlerMethodAdapter 抽象类。
为什么要有这层抽象?让我们回过头看看 《精尽 Spring MVC 源码解析 —— HandlerMapping 组件(三)之 AbstractHandlerMethodMapping》 就会明白:
- AbstractHandlerMethodMapping 对应 「6. AbstractHandlerMethodAdapter」 。
- RequestMappingInfoHandlerMapping 对应 「7. RequestMappingHandlerAdapter」 。
6.1 构造方法
// AbstractHandlerMethodAdapter.java |
6.2 supports
实现 #supports(Object handler)
方法,支持 HandlerMethod 类型的处理器。代码如下:
// AbstractHandlerMethodAdapter.java |
其中,
#supportsInternal(HandlerMethod handlerMethod)
方法,由子类实现。代码如下:// AbstractHandlerMethodAdapter.java
/**
* Given a handler method, return whether or not this adapter can support it.
* @param handlerMethod the handler method to check
* @return whether or not this adapter can adapt the given method
*/
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);- 关于 RequestMappingHandlerAdapter 类对该方法的实现,见 「7.3 supportsInternal」 。
6.3 handle
实现 #handle(HttpServletRequest request, HttpServletResponse response, Object handler)
方法,处理器请求。代码如下:
// AbstractHandlerMethodAdapter.java |
- 其中,
#handleInternal(...)
抽象方法,将handler
参数是 HandlerMethod 类型。关于 RequestMappingHandlerAdapter 类对该方法的实现,见 「7.5 handleInternal」 。
6.4 getLastModified
#getLastModified(HttpServletRequest request, Object handler)
方法,获得最后更新时间。代码如下:
// AbstractHandlerMethodAdapter.java |
- 套路同 「6.3 handle」 。
7. RequestMappingHandlerAdapter
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
,实现 BeanFactoryAware、InitializingBean 接口,继承 AbstractHandlerMethodAdapter 抽象类,基于 @RequestMapping
注解的 HandlerMethod 的 HandlerMethodAdapter 实现类。
7.1 构造方法
// RequestMappingHandlerAdapter.java |
- 属性比较多,先不着急看,有个印象即可。
- 另外,也是因为属性多,所以 RequestMappingHandlerAdapter 有大量的 setting 方法。
7.2 afterPropertiesSet
艿艿:这里我们会看到大量的 RequestMappingHandlerAdapter 的属性初始化。😈 当然,本小节还是不细讲。哈哈哈哈。
#afterPropertiesSet()
方法,进一步初始化 RequestMappingHandlerAdapter 。代码如下:
// RequestMappingHandlerAdapter.java |
<1>
处,调用#initControllerAdviceCache()
方法,初始化 ControllerAdvice 相关。详细解析,见 「7.2.1 initControllerAdviceCache」 。<2>
处,初始化argumentResolvers
属性。其中,#getDefaultArgumentResolvers()
方法,获得默认的 HandlerMethodArgumentResolver 数组。详细解析,见 「7.2.2 getDefaultArgumentResolvers」 。<3>
处,初始化initBinderArgumentResolvers
属性。其中,#getDefaultInitBinderArgumentResolvers()
方法,获得默认的 HandlerMethodArgumentResolver 数组。详细解析,见 「7.2.3 getDefaultInitBinderArgumentResolvers」 。<4>
处,初始化returnValueHandlers
属性。其中,#getDefaultReturnValueHandlers()
方法,获得默认的 HandlerMethodReturnValueHandler 数组。详细解析,见 「7.2.4 getDefaultReturnValueHandlers」 。
7.2.1 initControllerAdviceCache
#initControllerAdviceCache()
方法,初始化 ControllerAdvice 相关。代码如下:
// RequestMappingHandlerAdapter.java |
<1>
处,调用ControllerAdviceBean#findAnnotatedBeans(ApplicationContext context)
方法,扫描@ControllerAdvice
注解的 Bean 们,并将进行排序。可能有胖友不熟悉这个注解,可以看看 《Spring 3.2 新注解 @ControllerAdvice》 。<2>
处,遍历 ControllerAdviceBean 数组。<2.1>
处,扫描有@ModelAttribute
,无@RequestMapping
注解的方法,添加到modelAttributeAdviceCache
中。<2.2>
处,扫描有@InitBinder
注解的方法,添加到initBinderAdviceCache
中。<2.3>
处,如果是 RequestBodyAdvice 或 ResponseBodyAdvice 的子类,添加到requestResponseBodyAdviceBeans
中。
7.2.2 getDefaultArgumentResolvers
#getDefaultArgumentResolvers()
方法,获得默认的 HandlerMethodArgumentResolver 数组。见 传送门 。
7.2.3 getDefaultInitBinderArgumentResolvers
#getDefaultInitBinderArgumentResolvers()
方法,获得默认的 HandlerMethodArgumentResolver 数组。见 传送门 。
7.2.4 getDefaultReturnValueHandlers
#getDefaultReturnValueHandlers()
方法,获得默认的 HandlerMethodReturnValueHandler 数组。见 传送门 。
7.3 supportsInternal
实现 #supportsInternal()
接口,默认返回 true
。代码如下:
// RequestMappingHandlerAdapter.java |
7.4 getLastModifiedInternal
实现 #getLastModifiedInternal()
方法,默认返回 -1
。代码如下:
// RequestMappingHandlerAdapter.java |
7.5 handleInternal
实现 #handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
方法,处理请求。代码如下:
// RequestMappingHandlerAdapter.java |
<1>
处,调用父类 WebContentGenerator 的#checkRequest(ttpServletRequest request)
方法,校验请求是否合法。代码如下:// WebContentGenerator.java
protected final void checkRequest(HttpServletRequest request) throws ServletException {
// Check whether we should support the request method.
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
}
// Check whether a session is required.
if (this.requireSession && request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}- 主要是 HttpMethod 的类型和是否有 Session 的校验。
<2>
处,调用#invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
方法,调用 HandlerMethod 方法。详细解析,见 「7.5.1 invokeHandlerMethod」 。在
<2>
中,有一个通过synchronizeOnSession
属性,控制是否同步相同 Session 的逻辑,还是蛮有趣的。其中WebUtils#getSessionMutex(session)
方法,获得用来锁的对象。代码如下:// WebUtils.java
public static Object getSessionMutex(HttpSession session) {
Assert.notNull(session, "Session must not be null");
Object mutex = session.getAttribute(SESSION_MUTEX_ATTRIBUTE);
if (mutex == null) {
mutex = session;
}
return mutex;
}- 当然,因为锁是通过
synchronized
是 JVM 进程级,所以在分布式环境下,无法达到同步相同 Session 的功能。默认情况下,synchronizeOnSession
为false
。
- 当然,因为锁是通过
<3>
处,TODO 1015 WebContentGenerator
7.5.1 invokeHandlerMethod
#invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
方法,调用 HandlerMethod 方法。代码如下:
// RequestMappingHandlerAdapter.java |
- 因为,Spring MVC 提供了大量的特性,所以涉及的组件会不少。😈 我们主要先梳理好主流程,所以涉及的组件,还是先不详细解析。我们的目的是,看到怎么调用 HandlerMethod 方法的,即调用 Controller 的
@RequestMapping
注解的方法。 <1>
处,创建 ServletWebRequest 对象。<2>
处,调用#getDataBinderFactory(HandlerMethod handlerMethod)
方法,创建 WebDataBinderFactory 对象。TODO 1016 WebDataBinderFactory<3>
处,调用#getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory)
方法,创建 ModelFactory 对象。TODO 1017 ModelFactory<4>
处,调用#createInvocableHandlerMethod(HandlerMethod handlerMethod)
方法,创建 ServletInvocableHandlerMethod 对象,然后设置其属性。本文会对 ServletInvocableHandlerMethod 做简单的解析。当然,详细的解析,胖友可以后续看看 《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》 一文。<5>
处,创建 ModelAndViewContainer 对象,并初始其相关属性。TODO 1019 ModelAndViewContainer<6>
处,TODO 芋艿 1003 async<7>
处,TODO 芋艿 1003 async<8>
处,TODO 芋艿 1003 async【关键】
<9>
处,调用ServletInvocableHandlerMethod#invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs)
方法,执行调用。代码如下:// ServletInvocableHandlerMethod.java
/**
* Invoke the method and handle the return value through one of the
* configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// <x> 执行调用
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置响应状态码
setResponseStatus(webRequest);
// 设置 ModelAndViewContainer 为请求已处理,返回
if (returnValue == null) { // 返回 null
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(getResponseStatusReason())) { // 有 responseStatusReason
mavContainer.setRequestHandled(true);
return;
}
// 设置 ModelAndViewContainer 为请求未处理
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
// 处理器返回值
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}在
<x>
处,调用父类 InvocableHandlerMethod 的#invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs)
方法,执行调用。代码如下:// InvocableHandlerMethod.java
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// <y> 解析参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 执行调用
return doInvoke(args);
}
protected Object doInvoke(Object... args) throws Exception {
// <z1> 设置方法为可访问
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// <z2> 执行调用
return getBridgedMethod().invoke(getBean(), args);
} catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
} catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
} else if (targetException instanceof Error) {
throw (Error) targetException;
} else if (targetException instanceof Exception) {
throw (Exception) targetException;
} else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}<y>
处,调用#getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs)
方法,解析方法的参数值们。<z1>
处,设置方法为可访问。<z2>
处,反射调用@RequestMapping
注解的方法。😈 有一点忘记提了,InvocableHandlerMethod 是 HandlerMethod 的子类,所以通过 HandlerMethod 的#getBridgedMethod()
方法,可以获得对应的@RequestMapping
注解的方法。
<10>
处,TODO 芋艿 1003 async<11>
处,调用#getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest)
方法,获得 ModelAndView 对象。代码如下:// RequestMappingHandlerAdapter.java
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// <1> TODO 1017 ModelFactory
modelFactory.updateModel(webRequest, mavContainer);
// 情况一,如果 mavContainer 已处理,则返回“空”的 ModelAndView 对象。
if (mavContainer.isRequestHandled()) {
return null;
}
// 情况二,如果 mavContainer 未处,则基于 `mavContainer` 生成 ModelAndView 对象
ModelMap model = mavContainer.getModel();
// 创建 ModelAndView 对象,并设置相关属性
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
// TODO 1004 flashMapManager
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}<1>
处,TODO 1017 ModelFactory<2>
处,情况一,如果 mavContainer 已处理,则返回“空”的 ModelAndView 对象。<3>
处,情况二,如果 mavContainer 未处,则基于mavContainer
生成 ModelAndView 对象。- 😈 这个方法,涉及后续文章的内容,胖友可以先跳过,后续在回来理解。
<12>
处,标记请求完成。
666. 彩蛋
😈 头疼,HandlerAdapter 的处理过程,涉及的组件较多。后续的文章,我们慢慢一个一个梳理。
还是那句老话,先整体,后局部,一点一点慢慢来。
参考和推荐如下文章: