HandlerAdapter

前言

因?yàn)镠andler格式是不固定的扫皱,所以在處理請(qǐng)求時(shí)需要HandlerAdapter做適配,我們?cè)賮砘仡櫹翲andlerAdapter的定義乔煞。

public interface HandlerAdapter {

    /**
     * 判斷是否支持傳入的handler
     */
    boolean supports(Object handler);

    /**
     * 使用給定的handler處理請(qǐng)求
     */
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * 返回上次修改時(shí)間,可以返回-1表示不支持
     */
    long getLastModified(HttpServletRequest request, Object handler);

}

HandlerAdapter的實(shí)現(xiàn)有HttpRequestHandlerAdapter柒室、SimpleServletHandlerAdapter渡贾、SimpleControllerHandlerAdapter、AnnotationMethodHandlerAdapter(已廢棄)和RequestMappingHandlerAdapter雄右,今天我們主要來分析下RequestMappingHandlerAdapter空骚,因?yàn)镽equestMappingHandlerAdapter使用的最多,其余的幾個(gè)比較簡(jiǎn)單擂仍。

RequestMappingHandlerAdapter是和RequestMappingHandlerMapping配合使用的囤屹,我們先來看一下RequestMappingHandlerAdapter的繼承關(guān)系圖,然后再詳細(xì)分析逢渔。

RequestMappingHandlerAdapter繼承關(guān)系圖

AbstractHandlerMethodAdapter

supports方法

AbstractHandlerMethodAdapter實(shí)現(xiàn)了supports方法肋坚,可以看出支持的Handler是HandlerMethod,另外還附加了一個(gè)判斷方法supportsInternal肃廓,這個(gè)方法由RequestMappingHandlerAdapter實(shí)現(xiàn)智厌,其就是簡(jiǎn)單地返回true。

    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

handle方法

AbstractHandlerMethodAdapter實(shí)現(xiàn)了handle方法盲赊,就是簡(jiǎn)單地調(diào)用了模板方法handleInternal铣鹏,handleInternal方法由RequestMappingHandlerAdapter實(shí)現(xiàn)。

    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        return handleInternal(request, response, (HandlerMethod) handler);
    }

RequestMappingHandlerAdapter

初始化過程

RequestMappingHandlerAdapter實(shí)現(xiàn)了InitializingBean接口哀蘑,所以它的初始化入口是afterPropertiesSet方法诚卸。

    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);
        }
    }

可以看出主要是初始化了三個(gè)對(duì)象

  • argumentResolvers 用于給處理器方法和被@ModelAttribute注解的方法設(shè)置參數(shù)
  • initBinderArgumentResolvers 用于給被@InitBinder注解的方法設(shè)置參數(shù)
  • returnValueHandlers 用于將處理器返回值處理成ModelAndView類型

這里面又涉及了兩個(gè)組件HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler,這里我們不會(huì)具體深入的分析這兩個(gè)組件绘迁,以后專門做講解合溺。

使用過程

從上面的分析我們知道使用的入口是handleInternal方法,可以看出最重要的一句是invokeHandlerMethod缀台。

    protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        // 檢查HttpMethod是否支持棠赛,檢查是否需要session
        checkRequest(request);

        // 需要同步session
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            } else {
                prepareResponse(response);
            }
        }

        return mav;
    }

下面我們來具體看下invokeHandlerMethod

    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);
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            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();
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

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

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

這個(gè)方法非常復(fù)雜,我們先來解釋下幾個(gè)變量

  • WebDataBinderFactory 從名字就可以看出來是創(chuàng)建WebDataBinder用的,WebDataBinder用于參數(shù)綁定恭朗,主要用于參數(shù)與String之間的類型轉(zhuǎn)換。

  • ModelFactory 用來處理Model依疼,主要包含兩個(gè)功能痰腮,1. 在Handler處理之前對(duì)Model初始化,2. 請(qǐng)求結(jié)束以后對(duì)Model參數(shù)進(jìn)行更新

  • ServletInvocableHandlerMethod 它繼承自HandlerMethod律罢,并且可以直接執(zhí)行膀值,實(shí)際請(qǐng)求的處理就是通過它來執(zhí)行的,參數(shù)綁定误辑、處理請(qǐng)求以及返回值的處理都在里面完成

這三個(gè)變量創(chuàng)建完之后的工作還有三步(省略異步處理):

  1. 新建傳遞參數(shù)的ModelAndViewContainer容器沧踏,并將相應(yīng)的參數(shù)設(shè)置到Model中

  2. 執(zhí)行請(qǐng)求 invocableMethod.invokeAndHandle(webRequest, mavContainer);

3.請(qǐng)求完成后進(jìn)行一些后置處理 getModelAndView(mavContainer, modelFactory, webRequest);

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                                Object... providedArgs) throws Exception {

        // 處理請(qǐng)求
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        try {
            // 使用ReturnValueHandler處理返回值
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception ex) {
            throw ex;
        }
    }
    public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
                                   Object... providedArgs) throws Exception {

        // 處理得到方法參數(shù)
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        // 調(diào)用方法處理請(qǐng)求
        Object returnValue = doInvoke(args);
        return returnValue;
    }
    // org.springframework.web.method.support.InvocableHandlerMethod
    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
                                             Object... providedArgs) throws Exception {

        MethodParameter[] parameters = getMethodParameters();
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = resolveProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            // 使用ArgumentResolver處理參數(shù)
            if (this.argumentResolvers.supportsParameter(parameter)) {
                try {
                    args[i] = this.argumentResolvers.resolveArgument(
                            parameter, mavContainer, request, this.dataBinderFactory);
                    continue;
                } catch (Exception ex) {
                    throw ex;
                }
            }
            if (args[i] == null) {
                throw new IllegalStateException("Could not resolve method parameter at index " +
                        parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
                        ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
            }
        }
        return args;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市巾钉,隨后出現(xiàn)的幾起案子翘狱,更是在濱河造成了極大的恐慌,老刑警劉巖砰苍,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潦匈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡赚导,警方通過查閱死者的電腦和手機(jī)茬缩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吼旧,“玉大人凰锡,你說我怎么就攤上這事∪Π担” “怎么了掂为?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)员串。 經(jīng)常有香客問我菩掏,道長(zhǎng),這世上最難降的妖魔是什么昵济? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任智绸,我火速辦了婚禮,結(jié)果婚禮上访忿,老公的妹妹穿的比我還像新娘瞧栗。我一直安慰自己,他們只是感情好海铆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布迹恐。 她就那樣靜靜地躺著,像睡著了一般卧斟。 火紅的嫁衣襯著肌膚如雪殴边。 梳的紋絲不亂的頭發(fā)上憎茂,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音锤岸,去河邊找鬼竖幔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛是偷,可吹牛的內(nèi)容都是我干的拳氢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼蛋铆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼馋评!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起刺啦,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤留特,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后玛瘸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磕秤,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年捧韵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了市咆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡再来,死狀恐怖蒙兰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芒篷,我是刑警寧澤搜变,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站针炉,受9級(jí)特大地震影響挠他,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜篡帕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一殖侵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镰烧,春花似錦拢军、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春度陆,著一層夾襖步出監(jiān)牢的瞬間艾凯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工懂傀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趾诗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓鸿竖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親葱色。 傳聞我的和親對(duì)象是個(gè)殘疾皇子燃乍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容