前言
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它只负责准备数据,封装数据,而而不提供具体使用的方式方法~
看看它的继承树:
它主要有两个子类:InvocableHandlerMethod和ServletInvocableHandlerMethod,从命名就知道他俩都是有invoke调用能力的~