HandlerAdapter简介

1、HandlerAdapter接口继承结构体系

image.png

2、HandlerAdapter接口功能

HandlerAdapter接口中定义了三个方法

public interface HandlerAdapter {
    // 判断是否支持传入的handler
    boolean supports(Object handler);
    // 使用handler处理请求
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    // 获取资源的LastModified值
    long getLastModified(HttpServletRequest request, Object handler);
}

3、HandlerAdapter家族成员

从HandlerAdapter接口继承结构体系图中,我们可以发现HandlerAdapter家族的继承结构比较简单,其中AnnotationMethodHandlerAdapter已经被弃用,而SimpleControllerHandlerAdapterSimpleServletHandlerAdapterHttpRequestHandlerAdapter分别适配ControllerservletHttpRequest类型的Handler,方法非常简单,都是调用Handler里固定的方法。代码太过简单,就不过多介绍。剩下一个RequestMappingHandlerAdapter就比较复杂了,可以说,该类是整个SpringMVC中最复杂的类了,却也是目前SpringMVC中使用到的频率最高的类。目前在SpringMVC的使用过程中,对请求的处理主要就是依赖RequestMappingHandlerMappingRequestMappingHandlerAdapter类的配合使用。下面重点介绍下RequestMappingHandlerAdapter类。

4、RequestMappingHandlerAdapter

4.1、RequestMappingHandlerAdapter概述

RequestMappingHandlerAdapter继承自AbstractHandlerMethodAdapter,后者非常简单,三个接口方法分别调用了三个模板方法,这是典型的模板模式。其中supportsInternalgetLastModifiedInternal模板方法在RequestMappingHandlerAdapter中的实现非常简单,一个直接返回true,一个直接返回-1,这里就不过多讲解。最重要的是handleInternal方法,就是这个方法实际使用Handler处理请求。具体处理过程大致有三步:

1.备好处理器需要的参数
2.使用处理器处理请求
3.处理返回值,也就是将不同类型的返回值统一处理成ModelAndView类型
(PS:如果标注了@RequestBody注解,则在第三步处理返回值时,会直接操作response对象,输出结果,不会再返回ModelAndView对象)

4.2、RequestMappingHandlerAdapter初始化

RequestMappingHandlerAdapter实现了InitializingBean接口,Spring容器会自动调用其afterPropertiesSet方法。

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    initControllerAdviceCache();

    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

该方法主要是初始化RequestMappingHandlerAdapter类中比较重要的6个属性:

  • modelAttributeAdviceCacheinitBinderAdviceCache:分别缓存@ControllerAdvice注解的类里面注释了@ModelAttribute@InitBinder的方法,其中@ModelAttribute很少使用,而@InitBinder多用于注册校验器和类型转换器。
  • requestResponseBodyAdvice:用来保存实现了RequestBodyAdviceResponseBodyAdvice接口的类,这些类可以修改接受RequestBody和返回ResponseBody的类。
  • argumentResolvers:用于给处理器方法设置参数
  • initBinderArgumentResolvers:用于给注释了@InitBinder方法设置参数
  • returnValueHandlers:用于对处理器返回值进行相应处理

4.3 RequestMappingHandlerAdapter的使用

RequestMappingHandlerAdapter处理请求的入口方法是handleInternal,方法中真正执行请求的是invokeHandlerMethod方法,代码如下:

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            if (logger.isDebugEnabled()) {
                logger.debug("Found concurrent result value [" + result + "]");
            }
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}

方法中首先使用requestresponse创建了ServletWebRequest类型的webRequest,在argumentResolvers解析参数时使用的request就是这个webRequest。接着对WebDataBinderFactoryModelFactoryServletInvocableHandlerMethod三个类型的变量初始化。

  • WebDataBinderFactory
    WebDataBinderFactory的作用是用来创建WebDataBinder的,后者用于参数绑定,主要功能就是实现参数跟String之间的类型转换(还包括参数校验),argumentResolvers在进行参数解析的过程中会用到WebDataBinder
  • ModelFactory
    ModelFactory是用来处理Model的,主要包含两个功能:1. 在处理器具体处理之前对Model进行初始化;2.在处理完请求之后对Model参数进行更新。一般都是用户@ModelAttibute注解,目前基本不会用到。
  • ServletInvocableHandlerMethod
    ServletInvocableHandlerMethod类型非常重要,实际的处理就是通过他来执行的,参数绑定、处理请求、以及返回值处理都在他里面完成。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335