添加方法:(基於spring-boot 2.2.x)
-
如果要添加自定義Handler, 可以寫一個實現
WebMvcConfigurer
接口中addReturnValueHandlers()
的配置類,Spring容器啓動時,會讀取到這裏的配置,最終增加到RequestMappingHandlerAdapter
的customReturnValueHandlers
成員變量中。
但是執行到這些Handler的順序必定是在默認的Handler執行完之後。@Configuration public class MyHandlerConfig implements WebMvcConfigurer { @Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) { handlers.add(new MyHandler()); }
-
如果要改變順序,或者注入特定的幾種Handler,有兩種方法:
-
方法1:RequestMappingHandlerAdapter對象創建時,顯式調用
setReturnValueHandlers()
方法:做一個WebMvcConfigurationSupport
的子類,並且注入到容器中,並且重寫createRequestMappingHandlerAdapter()
方法:@Component public MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport { @Override protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = super.createRequestMappingHandlerAdapter(); List<HandlerMethodReturnValueHandler> myHandlerList = new ArrayList<>(); // 這裏自定義Handler的順序 myHandlerList.add(new MyHandler()); .... adapter.setReturnValueHandlers(myHandlerList); } }
-
方法2:直接將調用容器中的
RequestMappingHandlerAdapter
的setReturnValueHandlers()
方法重新設置Handler:@Configuration public class MyConfig { @Autowired RequestMappingHandlerAdapter requestMappingHandlerAdapter; @Bean public MyHandler createMyHandler() { MyHandler myHandler = new MyHandler(); ArrayList<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>(); returnValueHandlers.add(myHandler); requestMappingHandlerAdapter.setReturnValueHandlers(returnValueHandlers); return myHandler; } }
-
源碼分析(基於spring-webmvc-5.1.9.RELEASE)
-
HandlerMethodReturnValueHandler
是用來處理AbstractHandlerMethodMapping
所有映射的HandlerMethod
-
所有的
HandlerMethodReturnValueHandler
註冊在RequestMappingHandlerAdapter
的returnValueHandlers
成員變量中, 其類型爲HandlerMethodReturnValueHandlerComposite
, 它是一個複合Handler。另外還有一個List類型的customReturnValueHandlers
成員變量,用來註冊自定義的Handler。private List<HandlerMethodReturnValueHandler> customReturnValueHandlers; private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
-
RequestMappingHandlerAdapter
初始化時, 會調用getDefaultReturnValueHandlers()
方法:添加完一系列默認的Handler之後,再調用getCustomReturnValueHandlers()
方法註冊自定義的Handler。private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(); // Single-purpose return value types handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(), this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager)); handlers.add(new StreamingResponseBodyReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice)); handlers.add(new HttpHeadersReturnValueHandler()); 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, this.requestResponseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); // Custom return value types 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; }
-
customReturnValueHandlers
可通過setCustomReturnValueHandlers
()方法設置,這個方法是在WebMvcConfigurationSupport
類中requestMappingHandlerAdapter()
方法中進行的, 可以複寫此類的addReturnValueHandlers()
方法設置需要註冊的Handler,而這個子類是DelegatingWebMvcConfiguration
,其內部有一個WebMvcConfigurerComposite
類型的複合配置configurers
,裏面包含了所有的實現了WebMvcConfigurer
接口的配置對象,最終會調到配置類的addReturnValueHandlers()
方法,把自定義的Handler加到一開始的customReturnValueHandlers
成員變量裏。 -
真正的去選取哪個Handler去調用
HandlerMethod
的邏輯是在ServletInvocableHandlerMethod
中調用HandlerMethodReturnValueHandlerComposite
進行的:// RequestMappingHandlerAdapter protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { .... ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); .... invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); .... invocableMethod.invokeAndHandle(webRequest, mavContainer); .... return getModelAndView(mavContainer, modelFactory, webRequest); } // ServletInvocableHandlerMethod public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); .... this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } // HandlerMethodReturnValueHandlerComposite public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); .... handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { return handler; } } return null; }
-
所有Handler的實現都沒有實現
Ordered
接口, 從注入默認到自定義,到最後的執行,也沒有對Handler進行排序。Handler的選取順序,全靠往List中add的順序。
SpringBoot默認注入的HandlerMethodReturnValueHandler列表
No | 類型 | 支持的Return Type |
---|---|---|
1 | ModelAndViewMethodReturnValueHandler | ModelAndView |
2 | ModelMethodProcessor | Model |
3 | ViewMethodReturnValueHandler | View |
4 | ResponseBodyEmitterReturnValueHandler | ResponseBodyEmitter | ResponseEntity <ResponseBodyEmitter> |
5 | StreamingResponseBodyReturnValueHandler | StreamingResponseBody | ResponseEntity <StreamingResponseBody> |
6 | HttpEntityMethodProcessor | HttpEntity |
7 | HttpHeadersReturnValueHandler | HttpHeaders |
8 | CallableMethodReturnValueHandler | Callable |
9 | DeferredResultMethodReturnValueHandler | DeferredResult, ListenableFuture, CompletionStage |
10 | AsyncTaskMethodReturnValueHandler | WebAsyncTask |
11 | ModelAttributeMethodProcessor | @ModelAttribute |
12 | RequestResponseBodyMethodProcessor | @ResponseBody |
13 | ViewNameMethodReturnValueHandler | CharSequence, void |
14 | MapMethodProcessor | Map |
15 | ModelAndViewResolverMethodReturnValueHandler | All (modelAndViewResolvers 不爲空) |
customReturnValueHandlers
註冊的Handler在No14和No15之間。
疑惑
有兩點比較疑惑
-
爲什麼注入的Handler不實現
Ordered
接口,不按照自己定義的順序去加入。 -
爲什麼
customReturnValueHandlers
的優先級那麼低,其實主要是No13的ViewNameMethodReturnValueHandler
,這時候如果返回了String或者void,這兩種很常見的returnType直接就被劫走了,根本到達不了自定義Handler那邊。
官方的Javadoc也明確這麼說了:A String return value can be interpreted in more than one ways depending on the presence of annotations like {@code @ModelAttribute} or {@code@ResponseBody}.
Therefore this handler should be configured after the handlers that support these annotations.
我猜測可能是這個SpringMVC自己的規則,不能破壞,如果要完全打破這個規則,那直接自定義一個 RequestMappingHandlerAdapter
同級別的Adapter,自己去定義這些規則。但是工作量可能會大很多。