2023-03-14  阅读(1)
原文作者:lifullmoon 原文地址:https://www.cnblogs.com/lifullmoon

HandlerAdapter 组件

HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器

由于 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:

HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod

本文是接着 《HandlerAdapter 组件(一)之 HandlerAdapter》 一文来分享 ServletInvocableHandlerMethod 组件。在 HandlerAdapter 执行处理器的过程中,主要的任务还是交由它来完成的。 ServletInvocableHandlerMethod 封装 HandlerMethod 处理器对象,它还包含 HandlerMethodArgumentResolver 参数解析器和 HandlerMethodReturnValueHandler 返回值处理器等组件。虽然内容不多,但还是有必要另一篇进行分析。

回顾

先来回顾一下 RequestMappingHandlerAdapter 是如何创建 ServletInvocableHandlerMethod 对象的,可以回到 《HandlerAdapter 组件(一)之 HandlerAdapter》 RequestMappingHandlerAdapter 小节下面的 invokeHandlerMethod 方法,如下:

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, 
                                               HandlerMethod handlerMethod) throws Exception {
        // ... 省略相关代码
        // <4> 创建 ServletInvocableHandlerMethod 对象,并设置其相关属性
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    	// ... 省略相关代码
        // <9> 执行调用
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        // ... 省略相关代码
    }
    
    protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
        return new ServletInvocableHandlerMethod(handlerMethod);
    }
  • HandlerMethod 处理器封装成 ServletInvocableHandlerMethod 对象,然后设置参数解析器和返回值处理器
  • 这里设置了 ServletInvocableHandlerMethod 对象的 resolversparameterNameDiscovererreturnValueHandlers 相关属性
  • 调用invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs)方法,执行处理器

类图

ServletInvocableHandlerMethod 的整体类图如下:

202303142235005141.png

依次分析

HandlerMethod

org.springframework.web.method.HandlerMethod,处理器的方法的封装对象

《HandlerMapping 组件(三)之 AbstractHandlerMethodMapping》 AbstractHandlerMethodMapping 小节中已经分析过该对象

InvocableHandlerMethod

org.springframework.web.method.support.InvocableHandlerMethod,继承 HandlerMethod 类,可 invoke 调用的 HandlerMethod 实现类。

也就是说,HandlerMethod 只提供了处理器的方法的基本信息,不提供调用逻辑。

构造方法

    public class InvocableHandlerMethod extends HandlerMethod {
        /** 无参时的入参,空数组 */
    	private static final Object[] EMPTY_ARGS = new Object[0];
        /** 数据绑定器工厂 */
    	@Nullable
    	private WebDataBinderFactory dataBinderFactory;
        /** 参数解析器组合对象 */
    	private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
    	/** 方法的参数名称发现器 */
    	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    }

invokeForRequest

invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,执行请求,方法如下:

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    
        // <1> 解析参数
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // <2> 执行调用
        return doInvoke(args);
    }
  1. 调用 getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,解析方法的参数们,如下:
        protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
            // 获得方法的参数
            MethodParameter[] parameters = getMethodParameters();
            // 无参,返回空数组
            if (ObjectUtils.isEmpty(parameters)) {
                return EMPTY_ARGS;
            }
            // 将参数解析成对应的类型
            Object[] args = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                // 获得当前遍历的 MethodParameter 对象,并设置 parameterNameDiscoverer 到其中
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                // <1> 先从 providedArgs 中获得参数。如果获得到,则进入下一个参数的解析,默认情况 providedArgs 不会传参
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] != null) {
                    continue;
                }
                 // <2> 判断 resolvers 是否支持当前的参数解析
                if (!this.resolvers.supportsParameter(parameter)) {
                    throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                }
                try {
                    // 执行解析,解析成功后,则进入下一个参数的解析
                    args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                }
                catch (Exception ex) {
                    // Leave stack trace for later, exception may actually be resolved and handled...
                    if (logger.isDebugEnabled()) {
                        String exMsg = ex.getMessage();
                        if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                            logger.debug(formatArgumentError(parameter, exMsg));
                        }
                    }
                    throw ex;
                }
            }
            return args;
        }
1.  先从 `providedArgs` 中获得参数。如果获得到,则进入下一个参数的解析。默认情况下,`providedArgs` 参数不会传递,所以可以暂时先忽略。保证核心逻辑的理解
2.  判断 `argumentResolvers` 是否支持当前的参数解析。如果支持,则进行解析。关于 HandlerMethodArgumentResolverComposite 的详细解析,见[ **《HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver》** ][_HandlerAdapter _ HandlerMethodArgumentResolver_]
  1. 调用 doInvoke(Object... args) 方法,执行方法的调用,方法如下:
        @Nullable
        protected Object doInvoke(Object... args) throws Exception {
            // <1> 设置方法为可访问
            ReflectionUtils.makeAccessible(getBridgedMethod());
            try {
                // <2> 执行调用
                return getBridgedMethod().invoke(getBean(), args);
            }
            catch (IllegalArgumentException ex) {
                assertTargetBean(getBridgedMethod(), getBean(), args);
                String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
                throw new IllegalStateException(formatInvokeError(text, args), ex);
            }
            catch (InvocationTargetException ex) {
                // Unwrap for HandlerExceptionResolvers ...
                Throwable targetException = ex.getTargetException();
                if (targetException instanceof RuntimeException) {
                    throw (RuntimeException) targetException;
                }
                else if (targetException instanceof Error) {
                    throw (Error) targetException;
                }
                else if (targetException instanceof Exception) {
                    throw (Exception) targetException;
                }
                else {
                    throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
                }
            }
        }
1.  设置方法为可访问
2.  通过反射执行该方法

注意,这里获取到的 Method 对象可能是桥接方法,桥接方法:如果泛型对象,编译器则会自动生成一个桥接方法(java1.5向后兼容)

ServletInvocableHandlerMethod

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod,继承 InvocableHandlerMethod 类,用于 DispatcherServlet 执行 HandlerMethod 处理器

构造方法

    public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    	private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");
        
        /** 返回结果处理器组合对象 */
    	@Nullable
    	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
        
        public ServletInvocableHandlerMethod(Object handler, Method method) {
    		super(handler, method);
    	}
    
    	public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
    		super(handlerMethod);
    	}
    }

invokeAndHandle

invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,处理请求,执行处理器, 并处理返回结果 ,方法如下:

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // <1> 执行调用
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        // <2> 设置响应状态码
        setResponseStatus(webRequest);
    
        // <3> 设置 ModelAndViewContainer 为请求已处理,返回,和 @ResponseStatus 注解相关
        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }
    
        // <4> 设置 ModelAndViewContainer 为请求未处理
        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");
        try {
            // <5> 处理返回值
            this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }
  1. 调用 InvocableHandlerMethod 父类的 invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,执行调用
  2. 调用 setResponseStatus(ServletWebRequest webRequest) 设置响应的状态码,方法如下:
        private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
            // 获得状态码,和 @ResponseStatus 注解相关
            HttpStatus status = getResponseStatus();
            if (status == null) {
                return;
            }
            // 设置响应的状态码
            HttpServletResponse response = webRequest.getResponse();
            if (response != null) {
                String reason = getResponseStatusReason();
                if (StringUtils.hasText(reason)) { // 有 reason ,则设置 status + reason
                    response.sendError(status.value(), reason);
                } else { // 无 reason ,则仅设置 status
                    response.setStatus(status.value());
                }
            }
        
            // To be picked up by RedirectView
            // 为了 RedirectView ,所以进行设置
            webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
        }
  1. 设置 ModelAndViewContainer 为请求已处理,返回,和 @ResponseStatus 注解相关
  2. 设置 ModelAndViewContainer 为请求未处理
  3. 调用 HandlerMethodReturnValueHandlerCompositehandleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) 方法,处理返回值。详细解析,见 《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》

总结

HandlerAdapter 执行 HandlerMethod 处理器的过程中,会将该处理器封装成 ServletInvocableHandlerMethod 对象,通过该对象来执行处理器。因为 HandlerMethod 处理器里面仅包含了方法的所有信息,如何解析参数、调用对应的方法、以及处理返回结果,它本身并不知道如何去处理,这里 Spring MVC 借助于 ServletInvocableHandlerMethod 对象来进行操作,原理就是通过反射机制执行该方法

其中涉及到的 HandlerMethodArgumentResolver 参数解析器和 HandlerMethodReturnValueHandler 返回值处理器是比较关键的两个组件,在接下来会逐个进行分析


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文