从原理层面掌握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调用能力的~


相关文章
|
2天前
|
设计模式 前端开发 Java
初识Spring MVC
初识Spring MVC
8 0
|
2天前
|
前端开发 Java 应用服务中间件
Spring MVC框架概述
Spring MVC 是一个基于Java的轻量级Web框架,采用MVC设计模型实现请求驱动的松耦合应用开发。框架包括DispatcherServlet、HandlerMapping、Handler、HandlerAdapter、ViewResolver核心组件。DispatcherServlet协调这些组件处理HTTP请求和响应,Controller处理业务逻辑,Model封装数据,View负责渲染。通过注解@Controller、@RequestMapping等简化开发,支持RESTful请求。Spring MVC具有清晰的角色分配、Spring框架集成、多种视图技术支持以及异常处理等优点。
9 1
|
4天前
|
监控 前端开发 Java
SpringBoot与SpringMVC有哪些区别?
SpringBoot和SpringMVC是Java开发中常用的两个框架,它们都是由Spring框架所提供的,但在功能和使用方式上有着一些区别。
17 2
|
6天前
|
Java 开发者 微服务
Spring Cloud原理详解
【5月更文挑战第4天】Spring Cloud是Spring生态系统中的微服务框架,包含配置管理、服务发现、断路器、API网关等工具,简化分布式系统开发。核心组件如Eureka(服务发现)、Config Server(配置中心)、Ribbon(负载均衡)、Hystrix(断路器)、Zuul(API网关)等。本文讨论了Spring Cloud的基本概念、核心组件、常见问题及解决策略,并提供代码示例,帮助开发者更好地理解和实践微服务架构。此外,还涵盖了服务通信方式、安全性、性能优化、自动化部署、服务网格和无服务器架构的融合等话题,揭示了微服务架构的未来趋势。
31 6
|
10天前
|
负载均衡 Java 开发者
Spring Cloud:一文读懂其原理与架构
Spring Cloud 是一套微服务解决方案,它整合了Netflix公司的多个开源框架,简化了分布式系统开发。Spring Cloud 提供了服务注册与发现、配置中心、消息总线、负载均衡、熔断机制等工具,让开发者可以快速地构建一些常见的微服务架构。
|
16天前
|
安全 Java API
Spring工厂API与原理
Spring工厂API与原理
35 10
|
28天前
|
数据采集 前端开发 Java
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
23 3
|
28天前
|
存储 前端开发 Java
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
14 1
|
28天前
|
前端开发 Java Spring
数据之桥:深入Spring MVC中传递数据给视图的实用指南
数据之桥:深入Spring MVC中传递数据给视图的实用指南
33 3
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
46 0