SpringBoot 註冊自定義HandlerMethodReturnValueHandler

添加方法:(基於spring-boot 2.2.x)

  • 如果要添加自定義Handler, 可以寫一個實現WebMvcConfigurer接口中addReturnValueHandlers()
    的配置類,Spring容器啓動時,會讀取到這裏的配置,最終增加到 RequestMappingHandlerAdaptercustomReturnValueHandlers成員變量中。
    但是執行到這些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:直接將調用容器中的RequestMappingHandlerAdaptersetReturnValueHandlers()方法重新設置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註冊在RequestMappingHandlerAdapterreturnValueHandlers成員變量中, 其類型爲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在No14No15之間。

疑惑

有兩點比較疑惑

  1. 爲什麼注入的Handler不實現Ordered接口,不按照自己定義的順序去加入。

  2. 爲什麼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,自己去定義這些規則。但是工作量可能會大很多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章