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
1ModelAndViewMethodReturnValueHandlerModelAndView
2ModelMethodProcessorModel
3ViewMethodReturnValueHandlerView
4ResponseBodyEmitterReturnValueHandlerResponseBodyEmitter | ResponseEntity <ResponseBodyEmitter>
5StreamingResponseBodyReturnValueHandlerStreamingResponseBody | ResponseEntity <StreamingResponseBody>
6HttpEntityMethodProcessorHttpEntity
7HttpHeadersReturnValueHandlerHttpHeaders
8CallableMethodReturnValueHandlerCallable
9DeferredResultMethodReturnValueHandlerDeferredResult, ListenableFuture, CompletionStage
10AsyncTaskMethodReturnValueHandlerWebAsyncTask
11ModelAttributeMethodProcessor@ModelAttribute
12RequestResponseBodyMethodProcessor@ResponseBody
13ViewNameMethodReturnValueHandlerCharSequence, void
14MapMethodProcessorMap
15ModelAndViewResolverMethodReturnValueHandlerAll (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,自己去定义这些规则。但是工作量可能会大很多。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值