Springcloud学习笔记61---Spring MVC的拦截器HandlerInterceptor

1. HandlerMethod 介绍

HandlerMethod它作为Spring MVC的非公开API,可能绝大多数小伙伴都对它比较陌生,但我相信你对它又不是那么的生疏,因为你可能没用过但肯定见过。比如Spring MVC的拦截器HandlerInterceptor的拦截方法的第三个入参Object handler,虽然它是Object类型,但其实绝大部分情况下我们都会当作HandlerMethod来使用;又比如我之前的这篇讲RequestMappingHandlerMapping的文章也大量的提到过HandlerMethod这个类。

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

2.HandlerMethod详解

我们知道在 SpringMVC 中 控制器(Controller)负责对请求路径进行匹配并调用相应地执行方法。

而 HandlerMethod 就表示这个方法, 但它一般也被称作处理器,即 handler。因为它身上包含了控制器信息、控制器的指定方法信息。

在 Spring MVC 框架中,HandlerMethod 是一个类,用于表示一个处理请求的控制器方法。它包含了控制器类的实例以及要调用的方法的相关信息。

HandlerMethod 类提供了以下一些主要的属性和方法:

  • Bean:获取处理请求的控制器类的实例。
  • Method:获取要调用的处理方法对象。
  • getMethodParameters():获取处理方法的参数信息。
  • getMethodAnnotation(Class<T> annotationType):检查方法上是否有特定注解。
  • getBeanType():获取处理请求的控制器类的类型。

通过使用 HandlerMethod 对象,Spring MVC 可以确定哪个控制器方法来处理进入的 HTTP 请求,并利用相关信息进行方法调用和参数绑定。

简单来说可以理解为路由匹配的Controller方法的一个实例对象这个条件语句检查当前处理器对象(handler)是否是 HandlerMethod 的实例。

下面来看它的构造函数,需要关注的是它的几个成员变量:

public HandlerMethod(Object bean, Method method) {
    // 省略部分代码...

    // 控制器名称
    this.bean = bean;

    // 所在容器,这里指 SpringMVC 容器
    this.beanFactory = null;

    // 控制器类型
    this.beanType = ClassUtils.getUserClass(bean);

    // 路径匹配的执行方法
    this.method = method;

    // 方法参数
    this.parameters = initMethodParameters();

    // 桥方法,一般情况与 method 值一样
    this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);

    this.resolvedFromHandlerMethod = null;

}

 实例研究:

假设 HandlerMethod 表示 test 方法:

Controller
public class LoginController {
    @RequestMapping(value = "/login")
    public Object test(HttpServletRequest request, Model model) throws Exception {
        return null;
    }
}

则它的成员变量对应如下:

bean= LoginController 

beanType= Class<T> (com.apps.modules.login.controller.LoginController)

method= 
    public java.lang.Object com.apps.modules.login.controller.LoginController.test
    (javax.servlet.http.HttpServletRequest,org.springframework.ui.Model) 
    throws java.lang.Exception

parameters= 
    [org.springframework.web.method.HandlerMethod$HandlerMethodParameter@c9d41104, 
    org.springframework.web.method.HandlerMethod$HandlerMethodParameter@c9d41105]

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());
        }
    }
}

 参考文件:

https://blog.csdn.net/u012420654/article/details/59480498

https://www.cnblogs.com/yourbatman/p/11298197.html

posted @ 2024-01-26 15:28  雨后观山色  阅读(43)  评论(0编辑  收藏  举报