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