您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關spring webmvc請求處理流程中返回值處理是什么,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
講解3.2.x版本之后使用的RequestMappingHandlerAdapter,該類替換了AnnotationMethodHandlerAdapter。
自3.2之后的版本,引入了RequestMappingHandlerAdapter來替換了AnnotationMethodHandlerAdapter的處理。這里也來分析一下這個玩意兒。
因為也是一個HandlerAdapter,所以,前面的處理流程都是一樣的,servlet的getHandlerAdapter這個時候就返回了RequestMappingHandlerAdapter,而不是AnnotationMethodHandlerAdapter了。
拿到HandlerAdapter之后,我們就直接沖ha.handle()方法開始分析吧。
// 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方法。
//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類型的對象,繼續跟進。
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()方法就明白了。
到這里就不繼續跟了,我們熟悉的RequestResponseBodyMethodProcessor處理器處理結果的時候會設置mavContainer.setRequestHandled(true);表示處理已經完畢。
處理完之后,回到RequestMappingHandlerAdapter的invokeHandleMethod()方法。這個方法最終返回了getModelAndView();
從該方法中,我們可以看到,如果當前請求已經處理完成(mavContainer.isRequestHandled()值為true),則不進行后續的處理沒直接返回null,否則spring會繼續處理當前請求,并試圖返回一個ModelAndView。
既然上面提到了自定義的返回值處理器,那這個自定義是在哪里的呢?這個返回值處理器就是在哪里注冊的呢?精彩馬上回來!
返回值處理器需要實現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; }
從上可以看出,我們雖然我們的自定義返回值處理器放進去了,但是一定會用到嘛?那不一定哦。可以看到對所有的返回值處理器,并沒有進行排序,因此我們不能控制這個順序。先給一個調試的圖:
從圖中我們可看到,我們自定義的handler確實是加載進去了,但是由于spring在處理這個handlers的時候并沒有進行排序,所以我們自定義被放在了后面,我們不能夠控制這個順序(其他騷方法可以實現,這里不考慮這種情況)。所以存在一種情況,我們的返回值能夠被前面的11個處理器中的某一個處理,那么就輪不到我們自定義的返回值處理器了。舉個簡單的例子,比如你返回值類型是String,那么就會別ViewNameMethodReturnValueHandler處理;如果返回的類型是Map,那么就會被MapMethodProcessor處理,可以自行測試。因此我們的返回值其實也很重要。所以要使用到我們自定義的處理器,那么首先我們就得讓前面的處理器無法處理我們的返回結果。
通常可以實現一個通用的返回結果實體,或者某個標記接口(空接口),這樣其他的返回值處理器無法處理返回的值類型,這樣就輪到我們自己的返回值處理器了。也可以返回喜歡的實體類型,像示例中一樣使用注解也可以。但是因為通常一個項目來講返回值都是有定義的,返回的類型都是有同一種格式的,所以這類比較偏向使用通用相同的返回實體,或者使用某個接口標記。
over ...
上述就是小編為大家分享的spring webmvc請求處理流程中返回值處理是什么了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。