HandlerAdapter浅析

149 阅读6分钟

这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战

介绍

HandlerAdapterHandler的适配器,每种类型的Handler都对应一个HandlerAdapter,用于在获取到具体的逻辑处理器进行一些相关数据的转换,比如对参数列表的转换、对处理器处理的返回值的数据转换,不同容器下异步相关的处理等等。

分析

在顶层接口HandlerAdapter的实现中,基本上都是直接具体的实现。

HandlerAdapter类图png.png

HandlerAdapter有几个比较重要的接口:

  • supports:当前HandlerAdapter是否支持这个Handler
  • handle:利用Handler处理请求

这里以AbstractHandlerMethodAdapter为例进行分析。这个抽象类也比较简单,仅仅是将顶层接口中的参数进行具象化,再调用本类的模板方法,供子类直接使用,所以子类就不需要在进行类型相关的装换等操作。

如果handlerHandlerMethod, 直接强转后调用模板方法。

public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

通过上述的转换进入到这个扩展接口,这是个抽象方法,由子类具体进行实现。

protected abstract boolean supportsInternal(HandlerMethod handlerMethod);

处理请求接口:这里仅仅是将处理器的类型转换为处理器顶层接口类型后,就直接调用开放接口,具体的实现也是由子类实现。

public final ModelAndView handle(HttpServletRequest rq, HttpServletResponse rs, Object h) throws Exception {
    return handleInternal(rq, rs, (HandlerMethod) h);
}

RequestMappingHandlerAdapterHandlerAdapter体系中最复杂的类,包含了具体Handler执行前后需要特殊处理或者是一些开放的扩展点的处理。比如有参数处理器、参数名称处理器、请求消息体装换器、结果处理器等等。

1.初始化

RequestMappingHandlerAdapter的初始化分为两步,一步是在构造函数中进行默认HttpMessageConvert的添加,另一步是通过afterPropertiesSet后置初始化方法进行一些全局处理的缓存和一些组件的初始化。

  1. 初始化全局的三个缓存变量。
  2. 初始化参数解析器,以下获取接口中定义了一些常用的参数解析器,同时支持添加用户自定义参数解析器
  3. 初始化initBinder注解标记的方式参数绑定处理器,以下获取接口方法中可以获取到默认的解析器,同时支持添加用户自定义参数绑定器
  4. 初始化返回值处理器returnValueHandlers,以下方法获取可以获取到返回值解析器,同时支持添加用户自定义返回值处理器。
public void afterPropertiesSet() {
    // 1.
    initControllerAdviceCache();

    // 2. 
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }

    //3.
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }

    //4. 
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

初始化全局的三个参数解析器的缓存modelAttributeAdviceCacheinitBinderAdviceCacherequestResponseBodyAdvice。先从容器中获取出带有ControllerAdvice注解标记的所有容器对象。然后遍历这些对象,逐个对ModelAttribute注解、InitBinder注解,或者是RequestBodyAdvice或者是ResponseBodyAdvice的实现类的判断,然后分别添加到三个缓存器的添加处理。

  1. 若在上述解析过程中存在实现类, 则添加到最前面, 所以在requestResponseBodyAdvice中, 自定义的优先级高于默认
private void initControllerAdviceCache() {
		// ...
    // 1.
    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
}

2.实际处理

实际上核心处理在invokeHandlerMethod中,先对一些组件进行创建和赋值、初始化各种组件之后将数据传入invokeAndHandle()中。

protected ModelAndView invokeHandlerMethod(HttpServletRequest r, HttpServletResponse rs, HandlerMethod h) throws Exception {}

获取创建WebDataBinder实例的工厂类,其中WebDataBinder类用于绑定request参数到JavaBean对象,然后获取@ModelAttribute的非@RequestMapping方法,ServletInvocableHandlerMethod是继承于HandlerMethod 这里相当于只是将handlerMethod中的属性值赋值给invocableMethod, 仅仅只做了包了一层,接着就是为ServletInvocableHandlerMethod中的一些数据赋值, 增加了参数解析、处理器方法调用、返回值解析等逻辑,创建各种参数名称处理器、视图处理器、还有一些异步请求相关的处理。最后构建ModelAndView实例返回。

WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 
if (this.argumentResolvers != null) {
  invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
  invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}

ServletInvocableHandlerMethod就是具体的HandlerMethod的实现,所以在最后通过invocableMethod.invokeAndHandle(webRequest, mavContainer)调用到具体的实现。以下基本上就是具体Handler的调用前最后一步了,返回执行结果之后,由结果处理器进行处理。

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    //..
}

// 执行调用逻辑(这里会先经过参数解析器的处理之后,再通过反射调用到具体的方法)

invokeForRequest(webRequest, mavContainer, providedArgs);

进入返回值处理器进行相关处理

this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

这里想简要说明一下异步调用的场景:Servlet3.0支持服务器异步操作,在springmvc中也添加了响应的支持。整个请求的过程与同步类似,只是在具体的handler调用之后,如果是异步方式,假设是使用Callable,则这里获取到的返回值处理器就是CallableMethodReturnValueHandler。通过使用Callable方式异步处理逻辑。

WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(callable, mavContainer);

进入到返回值处理器CallableMethodReturnValueHandler后,将结果的callable对象使用webAsyncUtils进行异步处理。通过webAsyncTask.getExecutor()获取对应线程池。

  1. 默认情况下会创建DEFAULT_TASK_EXECUTOR ->SimpleAsyncTaskExecutor线程池

  2. 一般来说, 在RequestMappingHandlerAdapter中, 创建的WebAsyncManager时会传入它的this.taskExecutor

    2.1 而RequestMappingHandlerAdapter也会创建一个默认的SimpleAsyncTaskExecutor(名称前缀为MvcAsync)

    ​ 2.1.1 如果是在springmvc中, 初始化BeanRequestMappingHandlerAdapter时为它添加了单一线程池Executor.newSingleThreadExecutor

    ​ 2.1.2 如果是在springboot中, 初始化BeanRequestMappingHandlerAdapter时为它添加了一个名为applicationTaskExecutor的线程池

创建线程池对象,设置对应的拦截器,获取到具体的callable对象,创建拦截器链对象, 拦截器链对象一般情况下都会包含拦截列表、具体执行对象以及具体执行到某个拦截器的下标值,分别设置超时处理器、错误处理器、正常完成处理器,再设置一些标志位后将callable对象丢入线程池中,最后执行完成之后进行结果的分发, 这个具体在不同容器有不同的实现(tomcatundertow)

总结

​ 实际上处理器适配器就是一种适配器,所以每一种的处理器都需要配备一个适配器。它根据特殊的规则找到具体的处理器,是映射器与处理器之间的桥梁。