spring-webmvc請求處理流程——返回值處理

spring-webmvc請求處理流程——返回值處理

繼上一篇spring-webmvc請求處理流程以後,本篇繼續講解3.2.x版本以後使用的RequestMappingHandlerAdapter,該類替換了AnnotationMethodHandlerAdapter。java

RequestMappingHandlerAdapter

自3.2以後的版本,引入了RequestMappingHandlerAdapter來替換了AnnotationMethodHandlerAdapter的處理。這裏也來分析一下這個玩意兒。web

由於也是一個HandlerAdapter,因此,前面的處理流程都是同樣的,servlet的getHandlerAdapter這個時候就返回了RequestMappingHandlerAdapter,而不是AnnotationMethodHandlerAdapter了。spring

拿到HandlerAdapter以後,咱們就直接衝ha.handle()方法開始分析吧。json

// RequestMAppingHandlerAdapter.java
protected final ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    //...作一些檢查
    return invokeHandleMethod(request, response, handlerMethod);
}

private ModelAndView invokeHandleMethod(HttpServletRequest request,
             HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
	
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    //拿取咱們須要執行的controller的方法
    ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
	// 用於後面構造mv的Container
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
	//...這一段是對異步請求的處理
    //調用controller的方法,並處理mavContainer
    requestMappingMethod.invokeAndHandle(webRequest, mavContainer);

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

    return getModelAndView(mavContainer, modelFactory, webRequest);
}

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
                                     ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    modelFactory.updateModel(webRequest, mavContainer);
    //判斷,若是當前請求已經處理完成,則不進行後續的處理沒直接返回null
    if (mavContainer.isRequestHandled()) {
        return null;
    }
    //若是請求還未處理完成,那說明可能有頁面須要返回,開始查找,處理並返回mav
    ModelMap model = mavContainer.getModel();
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
    if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView());
    }
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
    }
    return mav;
}

直接跟進去。來到RequestMappingHAndlerAdapter的handleInternal()方法,不要驚慌,按照spring的一向風格,這TM固然不是核心方法,他只是作了一些檢查。方法最後調用了invokeHandleMethod()。在該方法中,作了一些所需參數的獲取,好比請求的controller層方法,參數。而後調用ServletInvocableHandlerMethod對象invokeAndHandle方法。mvc

調用業務方法,並處理返回值

//ServletInvocableHandlerMethod.java
public final void invokeAndHandle(ServletWebRequest webRequest,
       ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // 真正調用controller方法,得到結果
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	//設置響應狀態
    setResponseStatus(webRequest);
	//若是返回值爲null,則說明該請求不返回任何結果,直接設置mavContainer.setRequestHandled(true)
    //設置爲true以後,表示該請求已經處理完,後續再也不處理,後續會提到這個
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(this.responseReason)) {
        mavContainer.setRequestHandled(true);
        return;
    }
	//設置爲false,表示當前請求還未處理完成
    mavContainer.setRequestHandled(false);

    try {
        //調用默認的和自定義的全部返回值解析器處理返回結果
        this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {//...}
        throw ex;
    }
}

從註釋中可於瞭解到,首先調用業務邏輯得到返回結果,而後對返回值作必定的判斷並簡單處理。經過returnValueHandlers對象來進一步處理返回結果。這是個HandlerMethodReturnValueHandlerComposite類型的對象,繼續跟進。app

public void handleReturnValue(
        Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws Exception {
	// 得到能夠處理返回值的handler,固然也是經過遍歷,能夠看方法
    HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
    //若是沒有合適的返回值處理器,就會報錯
    Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
    //使用返回值處理器處理返回結果
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
    //遍歷全部handler,包括自定義的
    for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
        //...
        if (returnValueHandler.supportsReturnType(returnType)) {
            //若是該handler可以處理當前返回值,就返回該handler
            return returnValueHandler;
        }
    }
    return null;
}

來,看一下全部的默認註冊的處理器。看到了RequestResponseBodyMethodProcessor是否是很親切呢,對咯,他就是處理@ResponseBody註解的。喜歡的朋友能夠去看哈這個類的supportsReturnType()方法就明白了。異步

returnValueHandler

到這裏就不繼續跟了,咱們熟悉的RequestResponseBodyMethodProcessor處理器處理結果的時候會設置mavContainer.setRequestHandled(true);表示處理已經完畢。async

getModelAndView()

處理完以後,回到RequestMappingHandlerAdapter的invokeHandleMethod()方法。這個方法最終返回了getModelAndView();ide

從該方法中,咱們能夠看到,若是當前請求已經處理完成(mavContainer.isRequestHandled()值爲true),則不進行後續的處理沒直接返回null,不然spring會繼續處理當前請求,並試圖返回一個ModelAndView。測試

自定義HandlerMethodReturnValueHandler

既然上面提到了自定義的返回值處理器,那這個自定義是在哪裏的呢?這個返回值處理器就是在哪裏註冊的呢?精彩立刻回來!

自定義返回值處理器

返回值處理器須要實現HandlerMethodReturnValueHandler

public class MyCustomReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        //判斷方法是否包含自定義註解MyResonse或者返回結果是指定的某種類型
        return returnType.getMethodAnnotation(MyResponse.class) != null || ResponseResult.class.isAssignableFrom(returnType.getParameterType());
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // 代表該請求已經處理,後面spring不會再處理
        mavContainer.setRequestHandled(true);
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(JSON.toJSONString(returnValue));
    }
}

controller,我這裏註解和返回值類型都符合上面處理器的要求,事實上只要一種就行了。

@RequestMapping("/testValueHandler")
@MyResponse
public ResponseResult testValueHandler() {
	return new ResponseResult(0,"success");
}

springmvc.xml

<mvc:annotation-driven>
	<mvc:return-value-handlers>
		<bean class="com.wt.test.webmvc.config.MyCustomReturnValueHandler"/>
	</mvc:return-value-handlers>
</mvc:annotation-driven>

自定義返回結果處理器的初始化過程

從xml中能夠看到是自定義標籤mvc:return-value-handlers,跟蹤MvcNamespaceHandler。

// MvcNamespaceHandler.java
public void init() {
    //解析自定義標籤的parser
	registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
}

進入AnnotationDrivenBeanDefinitionParser的parse方法,自定義的就是在這裏處理的,咱們來看一下。

// AnnotationDrivenBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
	//...
    //獲取自定義的返回值處理器
    ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
    //...
    // 定義RequestMappingAdapterHandler
    RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
    //...
    if (returnValueHandlers != null) {
        //設置自定義返回值處理器的屬性customReturnValueHandlers
	handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
		}
    //...
    // 將定義的RequestMappingAdapterHandler註冊爲spring的bean
    parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
    //...
}

// 解析自定義標籤return-value-handlers
private ManagedList<?> getReturnValueHandlers(Element element, ParserContext parserContext) {
	Element handlersElement = DomUtils.getChildElementByTagName(element, "return-value-handlers");
	if (handlersElement != null) {
		return extractBeanSubElements(handlersElement, parserContext);
	}
	return null;
}

直接看註釋了,不解釋了。到這裏自定義的返回值處理器就已經註冊完了,已是spring的一個bean了。如今咱們來看看RequestMappingHandlerAdapter。

這個傢伙實現了InitializingBean,咱們來看afterPropertiesSet方法。

public void afterPropertiesSet() {
    //...
    }
    if (this.returnValueHandlers == null) {
        //獲取全部的返回值處理器,不要看方法名是getDefaultReturnVanlueHandler,實際上在裏面也包含了自定義的處理器
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
    initControllerAdviceCache();
}

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
	// 默認的返回值處理器
    // Single-purpose return value types
    handlers.add(new ModelAndViewMethodReturnValueHandler());
    handlers.add(new ModelMethodProcessor());
    handlers.add(new ViewMethodReturnValueHandler());
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
    handlers.add(new CallableMethodReturnValueHandler());
    handlers.add(new DeferredResultMethodReturnValueHandler());
    handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

    // Annotation-based return value types
    handlers.add(new ModelAttributeMethodProcessor(false));
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager));

    // Multi-purpose return value types
    handlers.add(new ViewNameMethodReturnValueHandler());
    handlers.add(new MapMethodProcessor());

    // 自定義的返回值處理器,就是在以前parse處理的那些
    if (getCustomReturnValueHandlers() != null) {
        handlers.addAll(getCustomReturnValueHandlers());
    }
    // 添加其餘的默認返回值處理器
    // Catch-all
    if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
        handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
    }
    else {
        handlers.add(new ModelAttributeMethodProcessor(true));
    }
    return handlers;
}

從上能夠看出,咱們雖然咱們的自定義返回值處理器放進去了,可是必定會用到嘛?那不必定哦。能夠看到對全部的返回值處理器,並無進行排序,所以咱們不能控制這個順序。先給一個調試的圖:

customReturnValueHandler

從圖中咱們可看到,咱們自定義的handler確實是加載進去了,可是因爲spring在處理這個handlers的時候並無進行排序,因此咱們自定義被放在了後面,咱們不可以控制這個順序(其餘騷方法能夠實現,這裏不考慮這種狀況)。因此存在一種狀況,咱們的返回值可以被前面的11個處理器中的某一個處理,那麼就輪不到咱們自定義的返回值處理器了。舉個簡單的例子,好比你返回值類型是String,那麼就會別ViewNameMethodReturnValueHandler處理;若是返回的類型是Map,那麼就會被MapMethodProcessor處理,能夠自行測試。所以咱們的返回值其實也很重要。因此要使用到咱們自定義的處理器,那麼首先咱們就得讓前面的處理器沒法處理咱們的返回結果

一般能夠實現一個通用的返回結果實體,或者某個標記接口(空接口),這樣其餘的返回值處理器沒法處理返回的值類型,這樣就輪到咱們本身的返回值處理器了。也能夠返回喜歡的實體類型,像示例中同樣使用註解也能夠。可是由於一般一個項目來說返回值都是有定義的,返回的類型都是有同一種格式的,因此這類比較偏向使用通用相同的返回實體,或者使用某個接口標記。

over ...

相關文章
相關標籤/搜索