从原理层面掌握HandlerMethod、InvocableHandlerMethod、ServletInvocableHandlerMethod的使用【享学Spring MVC】(上)

简介: 从原理层面掌握HandlerMethod、InvocableHandlerMethod、ServletInvocableHandlerMethod的使用【享学Spring MVC】(上)

前言


HandlerMethod它作为Spring MVC的非公开API,可能绝大多数小伙伴都对它比较陌生,但我相信你对它又不是那么的生疏,因为你可能没用过但肯定见过。

比如Spring MVC的拦截器HandlerInterceptor的拦截方法的第三个入参Object handler,虽然它是Object类型,但其实绝大部分情况下我们都会当作HandlerMethod来使用;又比如我之前的这篇讲RequestMappingHandlerMapping的文章也大量的提到过HandlerMethod这个类。


经由我这么“忽悠”,你是否觉得它还是相对比较重要的一个类了呢?不管你信不信,反正我是这么认为的:HandlerMethod它是理解Spring MVC不可或缺的一个类,甚至可以说是你希望参与到Spring MVC的定制化里面来不可忽略的一个关键API。


HandlerMethod


HandlerMethod它不是一个接口,也不是个抽象类,且还是public的。HandlerMethod封装了很多属性,在访问请求方法的时候可以方便的访问到方法、方法参数、方法上的注解、所属类等并且对方法参数封装处理,也可以方便的访问到方法参数的注解等信息


// @since 3.1
public class HandlerMethod {
  // Object类型,既可以是个Bean,也可以是个BeanName
  private final Object bean;
  // 如果是BeanName,拿就靠它拿出Bean实例了~
  @Nullable
  private final BeanFactory beanFactory;
  private final Class<?> beanType; // 该方法所属的类
  private final Method method; // 该方法本身
  private final Method bridgedMethod; // 被桥接的方法,如果method是原生的,它的值同method
  // 封装方法参数的类实例,**一个MethodParameter就是一个入参**
  // MethodParameter也是Spring抽象出来的一个非常重要的概念
  private final MethodParameter[] parameters;
  @Nullable
  private HttpStatus responseStatus; // http状态码(毕竟它要负责处理和返回)
  @Nullable
  private String responseStatusReason; // 如果状态码里还要复数原因,就是这个字段  可以为null
  // 通过createWithResolvedBean()解析此handlerMethod实例的handlerMethod。
  @Nullable
  private HandlerMethod resolvedFromHandlerMethod;
  // 标注在**接口入参**上的注解们(此处数据结构复杂,List+二维数组)
  @Nullable
  private volatile List<Annotation[][]> interfaceParameterAnnotations;
  // 它的构造方法众多  此处我只写出关键的步骤
  public HandlerMethod(Object bean, Method method) {
    ...
    this.beanType = ClassUtils.getUserClass(bean);
    this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
    this.parameters = initMethodParameters();
    ...
    evaluateResponseStatus();
  }
  // 这个构造方法抛出了一个异常NoSuchMethodException 
  public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
    ...
    this.method = bean.getClass().getMethod(methodName, parameterTypes);
    this.parameters = initMethodParameters();
    ...
    evaluateResponseStatus();
  }
  // 此处传的是BeanName
  public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
    ...
    // 这部判断:这个BeanName是必须存在的
    Class<?> beanType = beanFactory.getType(beanName);
    if (beanType == null) {
      throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
    }
    this.parameters = initMethodParameters();
    ...
    evaluateResponseStatus();
  }
  // 供给子类copy使用的
  protected HandlerMethod(HandlerMethod handlerMethod) { ... }
  // 所有构造都执行了两个方法:initMethodParameters和evaluateResponseStatus
  // 初始化该方法所有的入参,此处使用的是内部类HandlerMethodParameter
  // 注意:处理了泛型的~~~
  private MethodParameter[] initMethodParameters() {
    int count = this.bridgedMethod.getParameterCount();
    MethodParameter[] result = new MethodParameter[count];
    for (int i = 0; i < count; i++) {
      HandlerMethodParameter parameter = new HandlerMethodParameter(i);
      GenericTypeResolver.resolveParameterType(parameter, this.beanType);
      result[i] = parameter;
    }
    return result;
  }
  // 看看方法上是否有标注了@ResponseStatus注解(接口上或者父类 组合注解上都行)
  // 若方法上没有,还会去所在的类上去看看有没有标注此注解
  // 主要只解析这个注解,把它的两个属性code和reason拿过来,最后就是返回它俩了~~~
  // code状态码默认是HttpStatus.INTERNAL_SERVER_ERROR-->(500, "Internal Server Error")
  private void evaluateResponseStatus() {
    ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
    if (annotation == null) {
      annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
    }
    if (annotation != null) {
      this.responseStatus = annotation.code();
      this.responseStatusReason = annotation.reason();
    }
  }
  ... // 省略所有属性的get方法(无set方法)
  // 返回方法返回值的类型  此处也使用的MethodParameter 
  public MethodParameter getReturnType() {
    return new HandlerMethodParameter(-1);
  }
  // 注意和上面的区别。举个列子:比如方法返回的是Object,但实际return “fsx”字符串
  // 那么上面返回永远是Object.class,下面你实际的值是什么类型就是什么类型
  public MethodParameter getReturnValueType(@Nullable Object returnValue) {
    return new ReturnValueMethodParameter(returnValue);
  }
  // 该方法的返回值是否是void
  public boolean isVoid() {
    return Void.TYPE.equals(getReturnType().getParameterType());
  }
  // 返回标注在方法上的指定类型的注解   父方法也成
  // 子类ServletInvocableHandlerMethod对下面两个方法都有复写~~~
  @Nullable
  public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
    return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);
  }
  public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) {
    return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
  }
  // resolvedFromHandlerMethod虽然它只能被构造进来,但是它实际是铜鼓调用下面方法赋值
  @Nullable
  public HandlerMethod getResolvedFromHandlerMethod() {
    return this.resolvedFromHandlerMethod;
  }
  // 根据string类型的BeanName把Bean拿出来,再new一个HandlerMethod出来~~~这才靠谱嘛
  public HandlerMethod createWithResolvedBean() {
    Object handler = this.bean;
    if (this.bean instanceof String) {
      Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
      String beanName = (String) this.bean;
      handler = this.beanFactory.getBean(beanName);
    }
    return new HandlerMethod(this, handler);
  }
  public String getShortLogMessage() {
    return getBeanType().getName() + "#" + this.method.getName() + "[" + this.method.getParameterCount() + " args]";
  }
  // 这个方法是提供给内部类HandlerMethodParameter来使用的~~ 它使用的数据结构还是蛮复杂的
  private List<Annotation[][]> getInterfaceParameterAnnotations() {
    List<Annotation[][]> parameterAnnotations = this.interfaceParameterAnnotations;
    if (parameterAnnotations == null) {
      parameterAnnotations = new ArrayList<>();
      // 遍历该方法所在的类所有的实现的接口们(可以实现N个接口嘛)
      for (Class<?> ifc : this.method.getDeclaringClass().getInterfaces()) {
        // getMethods:拿到所有的public的方法,包括父接口的  接口里的私有方法可不会获取来
        for (Method candidate : ifc.getMethods()) {
          // 判断这个接口方法是否正好是当前method复写的这个~~~
          // 刚好是复写的方法,那就添加进来,标记为接口上的注解们~~~
          if (isOverrideFor(candidate)) {
            // getParameterAnnotations返回的是个二维数组~~~~
            // 因为参数有多个,且每个参数前可以有多个注解
            parameterAnnotations.add(candidate.getParameterAnnotations());
          }
        }
      }
      this.interfaceParameterAnnotations = parameterAnnotations;
    }
    return parameterAnnotations;
  }
  // 看看内部类的关键步骤
  protected class HandlerMethodParameter extends SynthesizingMethodParameter {
    @Nullable
    private volatile Annotation[] combinedAnnotations;
    ...
    // 父类只会在本方法拿,这里支持到了接口级别~~~
    @Override
    public Annotation[] getParameterAnnotations() {
      Annotation[] anns = this.combinedAnnotations;
      if (anns == null) { // 都只需要解析一次
        anns = super.getParameterAnnotations();
        int index = getParameterIndex();
        if (index >= 0) { // 有入参才需要去分析嘛
          for (Annotation[][] ifcAnns : getInterfaceParameterAnnotations()) {
            if (index < ifcAnns.length) {
              Annotation[] paramAnns = ifcAnns[index];
              if (paramAnns.length > 0) {
                List<Annotation> merged = new ArrayList<>(anns.length + paramAnns.length);
                merged.addAll(Arrays.asList(anns));
                for (Annotation paramAnn : paramAnns) {
                  boolean existingType = false;
                  for (Annotation ann : anns) {
                    if (ann.annotationType() == paramAnn.annotationType()) {
                      existingType = true;
                      break;
                    }
                  }
                  if (!existingType) {
                    merged.add(adaptAnnotation(paramAnn));
                  }
                }
                anns = merged.toArray(new Annotation[0]);
              }
            }
          }
        }
        this.combinedAnnotations = anns;
      }
      return anns;
    }
  }
  // 返回值的真正类型~~~
  private class ReturnValueMethodParameter extends HandlerMethodParameter {
    @Nullable
    private final Object returnValue;
    public ReturnValueMethodParameter(@Nullable Object returnValue) {
      super(-1); // 此处传的-1哦~~~~ 比0小是很有意义的
      this.returnValue = returnValue;
    }
    ...
    // 返回值类型使用returnValue就行了~~~
    @Override
    public Class<?> getParameterType() {
      return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
    }
  }
}


可以看到HandlerMethod它持有的属性是非常多的,提供的能力也是很强的。

但是不知道小伙伴有没有发现,虽然它持有了目标的Method,但是它并没有提供invoke执行它的能力,如果你要执行它还得自己把Method拿去自己执行。


@ResponseStatus注解在执行目标处理方法的时候会去计算此注解,因此我个人建议此注解请慎用。一般使用在Advice的全局异常处理上~


所以总的来说它的职责还是很单一的:HandlerMethod它只负责准备数据,封装数据,而而不提供具体使用的方式方法~


看看它的继承树:

image.png

它主要有两个子类:InvocableHandlerMethod和ServletInvocableHandlerMethod,从命名就知道他俩都是有invoke调用能力的~


相关文章
|
11天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
23 0
|
1月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
1月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
134 9
|
1月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
57 2
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
134 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
1月前
|
XML 前端开发 Java
拼多多1面:聊聊Spring MVC的工作原理!
本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖
43 0
|
1月前
|
负载均衡 Java API
Spring Cloud原理详解
Spring Cloud原理详解
73 0
|
1月前
|
负载均衡 Java 网络架构
Spring Cloud原理详解
介绍了Spring Cloud的原理和核心组件,包括服务注册与发现、配置管理、负载均衡、断路器、智能路由、分布式消息传递、分布式追踪和服务熔断等,旨在帮助开发人员快速构建和管理微服务架构中的分布式系统。
58 0
|
3月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
49 0
下一篇
无影云桌面