SpringMVC之源码分析--HandlerAdapter(二)

侯览
• 阅读 2444

概述

本章我们主要分析Spring处理HandlerAdapter组件的处理流程以及其接口源码。概括来说,Spring使用HandlerAdapter组件分为两步,首先是注册组件,其次是处理用户请求,以下针对这两个过程进行详细的分析。

本系列文章是基于Spring5.0.5RELEASE。

注册HandlerAdapter

一般情况下,在使用Spring MVC时,我们会配置在应用启动时加载和初始化Spring MVC组件,也就是在部署描述文件中配置<load-on-startup>1</load-on-startup>,启动过程会最终调用到DispatcherServlet的initStrategies(context)方法,此方法即为初始化九大组件的入口,当然也包括我们今天说要分析的HandlerAdapter,源码如下:

/**
 * 初始化策略对象
 */
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    // 初始化处理器适配器HandlerAdapter
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;
    // 在部署描述文件中可控制该参数
    if (this.detectAllHandlerAdapters) {
        // 从应用上下文中查找HandlerAdapter
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            // 对使用的HandlerAdapter进行排序,spring提供的只有RequestMappingHandlerAdapter实现了Ordered接口,其他都不具备排序功能
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    }
    else {
        try {
            // 如果在部署描述文件中配置了detectAllHandlerAdapters=false,此时spring会加载名称为handlerAdapter的bean为处理器适配器
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            // 转化为集合赋给handlerAdapters属性
            this.handlerAdapters = Collections.singletonList(ha);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerAdapter later.
        }
    }

    // 如果未配置HandlerAdapter,注册默认的处理器适配器,即从DispatcherServlet.properties中获取的HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter和ReqeustMappingHandlerAdapter
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isDebugEnabled()) {
            logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
        }
    }
}

以上就是Spring MVC对HandlerAdapter组件的注册过程。

处理请求

应用在启动时完成了HandlerAdapter的注册,即具备了处理用户请求的能力,那么在用户发起请求时,请求会有DispatcherSerlvlet所拦截,最终调用其doDispatch方法进行处理,源码如下:

/**
 * 处理请求分发给handler
 */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 附件上传有关,后续分析multipartResolver时再详细分析
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 获取请求处理的HandlerExecutionChain对象,该对象组装了我们的handler和相关拦截器
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // 获取请求处理的处理器适配器,在getHandlerAdapter方法中进行适配策略的判断
            // 参加下面getHandlerAdapter的方法详解
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (logger.isDebugEnabled()) {
                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            // 拦截器处理用户请求,即执行请求相关的拦截器方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 调用handler处理方法,由此,通过适配器模式就调用到了我们使用的handler的处理方法
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

/**
 * 返回handler对象的处理器适配器
 */
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        // 迭代处理器适配器策略,判断handler是否适配成功
        for (HandlerAdapter ha : this.handlerAdapters) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing handler adapter [" + ha + "]");
            }
            // 进行适配策略的判断
            if (ha.supports(handler)) {
                return ha;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

以上就是HandlerAdatper处理用户请求源码分析。

接口分析

通过前面两部分,我们分析了Spring MVC对HandlerAdapter组件的使用,包括注册和处理请求过程,接下来我们看一下给接口的定义,源码如下:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerAdapter {
    
    /**
     * 判断适配器是否适配handler,适配策略由子类实现
     */
    boolean supports(Object handler);

    /*
     * 使用适配的handler执行用户请求
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * 返回资源的最后修改时间,如果handler实现类不支持可以返回-1
     */
    long getLastModified(HttpServletRequest request, Object handler);

}

以上是HandlerAdapter接口的源码分析,如需自定义HandlerAdapter,只需要实现该接口,在supports方法中定义适配策略,并实现handle方法进行调用即可。

总结

本文主要分析了Spring MVC使用HandlerAdapter组件处理用户请求的过程,从过程来看,用户可干预的也就是实现HanderApater接口,自定义处理器适配器。

接下来的几章将分析Spring MVC提供的HandlerAdapter适配策略,希望本节对大家能有帮助,谢谢。

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

SpringMVC之源码分析--HandlerAdapter(二)

点赞
收藏
评论区
推荐文章
Easter79 Easter79
3年前
spring源码分析系列 (2) spring拓展接口BeanPostProcessor
Spring更多分析spring源码分析系列(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fxiaoxing%2Fp%2F10249180.html)主要分析内容:一、BeanPostProcessor简述与demo示例二
Easter79 Easter79
3年前
SpringMVC处理静态文件源码分析
SpringMVC处理静态资源,主要是两个标签,mvc:resources和mvc:defaultservlethandler。在详细说明他们的原理之前,需要先简单说明下SpringMVC中请求处理机制:HandlerMapping和HandlerAdapter。1HandlerMapping和HandlerAdapter的来由用过pytho
Stella981 Stella981
3年前
Spring MVC请求处理流程分析
一、简介SpringMVC框架在工作中经常用到,配置简单,使用起来也很方便,很多书籍和博客都有介绍其处理流程,但是,对于其原理,总是似懂非懂的样子。我们做技术,需要做到知其然,还要知其所以然。今天我们结合源码来深入了解一下SpringMVC的处理流程。!(https://www.codenuclear.com/wpcontent/
Easter79 Easter79
3年前
SpringMVC源码阅读系列汇总
1.前言1.1导入SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道!(https://oscimg.oschina.net/oscnet/0c6bfd66042ee7eff6e4aa69652e7c93a7f.png)本文基于Spring4.3.7源码分析,(不
Stella981 Stella981
3年前
Spring MVC 解读——@RequestMapping (2)
SpringMVC解读——@RequestMapping    上一篇文章中我们了解了Spring如何处理@RequestMapping注解,并将请求映射信息保存到系统中以处理客户端发送来的请求,但是Spring是怎样接受请求,并根据请求URL来匹配正确的处理器方法呢,更重要的是Sprin
Stella981 Stella981
3年前
JetPack之ViewModel最新源码详细分析
本文会基于最新版ViewModel使用方法与源码进行详细分析,从注册到实现ViewModel界面数据如何保存与管理全部涉及。\\简介:\\ViewModel是JetPack系列库之一,它用来对组件的界面数据进行管理,且当组件的状态发生改变时数据依然留存。优点:1.当所依赖组件的状态发生改变时,例如屏幕旋转等,界面数据不会发
Stella981 Stella981
3年前
Kafka源码系列之Broker的IO服务及业务处理
Kafka源码系列之Broker的IO服务及业务处理一,kafka角色Kafka源码系列主要是以kafka0.8.2.2源码为例。以看spark等源码的经验总结除了一个重要的看源码的思路:先了解部件角色和功能角色,然后逐个功能请求序列画图分析,最后再汇总。那么,下面再啰嗦一下,kafka的角色。kafka在生产中的使用,如下
Easter79 Easter79
3年前
SpringMVC源码深度解析之HandlerAdapter适配器模式源码分析
!(https://gss2.bdstatic.com/fo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/signed6ee77cff039245a1b5e609bfafc3ff/d52a2834349b033b7515ed6b16ce36d3d439bde5.jpg)
Easter79 Easter79
3年前
Spring依赖处理过程源码分析
1.AbstractAutowireCapableBeanFactorydoCreateBean创建Bean2.AbstractAutowireCapableBeanFactorypopulateBean属性的核心注入方法3.InstantiationAwareBeanPostProcessorpostProcessProperties
智多星V+TNY264278 智多星V+TNY264278
9个月前
淘宝商品数据分析宝典:解锁销售与供应链优化秘籍
对淘宝商品通过API接口的数据进行详细分析,是一个涉及数据收集、处理、分析和结论的复杂过程。以下是一个详细的分析框架,包括需要分析的具体数据、数据来源、重要性以及实用建议。一、分析过程1.数据收集:首先,需要选择合适的API接口来获取淘宝商品数据。淘宝开放
批量创建云主机的整个过程
本文分享自天翼云开发者社区《》,作者:乐道上次我们讲述了云主机创建的流程,整个过程中并没有详细区分各个组件的基本功能,本章节将会为大家详细讲述批量创建过程中各个组件的处理过程。1、我们通过console或openapi进行批量创建云主机的下单操作,例如批量