SpringMVC工作原理之二:HandlerMapping和HandlerAdapter

double

记录一下

一、HandlerMapping

作用是根据当前的请求找到对应 Handler, 并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到 HandlerExecutionChain 对象中,在 HandlerMapping 接口的内部只有一个方法,如下:

HandlerExecutionChain getHandler(HttpServletRequest request);

HandlerMapping 是由 DispatcherServlet 调用,DispatcherServlet 会从容器中取出所有 HandlerMapping 实例并遍历,让 HandlerMapping 实例根据自己实现类的方式去尝试查找 Handler,以下是 HandlerMapping 具体的一些实现类。

@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 这些 handlerMapping 在容器初始化时创建,在 initHandlerMappings 时放入集合中 for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } }
该方法在 org.springframework.web.servlet.DispatcherServlet 中

另外上面说到的 Handler 有可能是一个 HandlerMethod (封装了 Controller 中的方法)对象,也有可能是一个 Controller 对象、HttpRequestHandler 对象或 Servlet 对象,而这个 Handler 具体是什么对象,也是与使用的 HandlerMapping 实现类有关,从下面图所示可以看到 HandlerMapping 实现类有两个分支,分别继承自 AbstractHandlerMethodMapping(得到HandlerMethod)和 AbstractUrlHandlerMapping(得到 HttpRequestHandler、Controller 或 Servlet),它们又统一继承于 AbstractHandlerMapping。
AbstractHandlerMapping.png

AbstractHandlerMapping 它实现了 HandlerMapping 接口中的 getHandler() 方法,具体源码如下:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 根据请求获取执行程序,具体的获取方式由子类决定,getHandlerInternal() 是抽象方法 Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } // 将 Handler 与一堆拦截器包装到 HandlerExecutionChain 对象中 if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }

可以看到这这个方法中又调用了 getHandlerInternal() 方法获取到了 Handler 对象,而 Handler 对象具体内容是由它的子类去定义的。下面就来看一下 AbstractHandlerMapping 的两个分支子类

1. AbstractUrlHandlerMapping

AbstractUrlHandlerMapping 这个分支获取的 Handler 的类型实际就是一个 Controller 类,所以一个 Controller 只能对应一个请求,源码如下所示

protected Object getHandlerInternal(HttpServletRequest request) throws Exception { // 根据当前请求获取"查找路径" String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 根据路径获取 Handler (即Controller),先尝试直接匹配,在尝试模式匹配 Object handler = lookupHandler(lookupPath, request); if (handler == null) { Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = obtainApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; }

2.AbstractHandlerMethodMapping

AbstractHandlerMethodMapping 这个分支获取的 Handler 的类型是 HandlerMethod, 即这个 Handler 是一个方法,它保存了方法的信息(如 Method),这样一个 Controller 就可以处理多个请求了,具体源码如下

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 根据当前请求获取 "查找路径" String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); this.mappingRegistry.acquireReadLock(); try { // 获取当前请求最佳匹配的处理方法(即 Controller 类的方法) HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }

上述代码中 lookupHandlerMethod() 方法主要工作时在 Map<T, HandlerMethod> handlerMethods 中找到 HandlerMethod, 这里的 T 是 HandlerMappingInfo,它封装了 @RequestMapping 注解中的信息。那 HandlerMethod 是怎么创建的(即怎么把 Controller 的方法变成了它),继续看一下源码找到 initHandlerMethods() 方法,这个方法是这个类创建后调用的,如下是它的源码

/** * 从容器中获取所有 Bean 的名称,detectHandlerMethodsInAncestorContexts 默认 false, * 不从父容器中查找,即默认只查找 SpringMVC 的 IOC 容器,不查找它的父容器 Spring 的 IOC 容器 */ protected String[] getCandidateBeanNames() { return (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); } /** * 映射方法 */ protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); } catch () {} if (beanType != null && isHandler(beanType)) { // 利用反射得到 Bean 中的 Method 并包装成 HandlerMethod, 然后放入 Map 中 detectHandlerMethods(beanName); } } protected void initHandlerMethods() { for (String beanName : getCandidateBeanNames()) { // 这里的 isHandler 方法由子类实现,判断是否拥有 @Controller 注解或 @RequestMapping 注解 if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }

看完上述代码后,可以知道是在 detectHandlerMethods() 方法中将 Bean 的方法转换为 HandlerMethod 对象,具体实现如下

protected void detectHandlerMethods(Object handler) { // 获取这个 Bean 的Class 对象 Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { // 获取代理对象的原始类型 Class<?> userType = ClassUtils.getUserClass(handlerType); // 获取 Method Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } // 注册 Method 和它的映射,RequestMappingInfo 存储着映射信息 methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }

最后在 registerHandlerMethod() 方法中,将 RequestMappingInfo 作为 key,把 Method 包装成 HandlerMethod 作为 value 添加到 Map<T, HandlerMethod> handlerMethods 中。

public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }

二、HandlerAdapter

根据 Handler 来找到支持它的 HandlerAdapter,通过 HandlerAdapter 执行这个 Handler 得到 ModelAndView 对象,HandlerAdapter 接口中的方法如下:

  • boolean supports(Object handler): 当前 HandlerAdapter 是否支持这个 Handler
  • ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception: 利用 Handler 处理请求
  • long getLastModified(HttpServletRequest request, Object handler)
    HandlerAdapter实现类.png

1、RequestMappingHandlerAdapter

从上面的文章中可以知道,利用 RequestMappingHandlerMapping 获取的 Handler 是 HandlerMethod 类型,它代表 Controller 里要执行的方法,而 RequestMappingHandlerAdapter 可以执行 HandlerMethod 对象。

RequestMappingHandlerAdapter 的 handler() 方法是在它的父类 AbstractHandlerMethodAdapter 类中实现的,源码如下:

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); }

handleInternal() 方法是由 RequestMappingHandlerMapping 自己来实现的,源码如下所示

protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // 是否需要在 synchronize 块中执行 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); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { // 是否通过 @SessionAttributes 注释声明了 session 属性。 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }

2、HttpRequestHandlerAdapter

HttpRequestHandlerAdapter 可以执行 HttpRequestHandler 类型的 Handler,源码如下

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; }

3、SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter 可以执行 Controller 类型的 Handler,源码如下

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); }

4、SimpleServletHandlerAdapter

SimpleServletHandlerAdapter 可以执行 Servlet 类型的 Handler,源码如下

@Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Servlet) handler).service(request, response); return null; }

三、HandlerExceptionResolver

负责处理异常的类,负责根据异常来设置 ModelAndView,然后交由 render 渲染页面。
HandlerExecptionResolver 接口中只有一个方法,如下:

  • ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

扩展阅读

HandlerMapping和HandlerAdapter