InvocableHandlerMethod
它是对HandlerMethod的扩展,增加了调用能力。这个能力在Spring MVC可是非常非常重要的,它能够在调用的时候,把方法入参的参数都封装进来(从HTTP request里,当然借助的必然是HandlerMethodArgumentResolver)
// @since 3.1 public class InvocableHandlerMethod extends HandlerMethod { private static final Object[] EMPTY_ARGS = new Object[0]; // 它额外提供的几个属性,可以看到和数据绑定、数据校验就扯上关系了~~~ // 用于产生数据绑定器、校验器 @Nullable private WebDataBinderFactory dataBinderFactory; // HandlerMethodArgumentResolver用于入参的解析 private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); // 用于获取形参名 private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); ... // 省略构造函数 全部使用super的 // 它自己的三大属性都使用set方法设置进来~~~并且没有提供get方法 // 也就是说:它自己内部使用就行了~~~ // 在给定请求的上下文中解析方法的参数值后调用该方法。 也就是说:方法入参里就能够自动使用请求域(包括path里的,requestParam里的、以及常规对象如HttpSession这种) // 解释下providedArgs作用:调用者可以传进来,然后直接doInvoke()的时候原封不动的使用它 //(弥补了请求域没有所有对象的不足,毕竟有些对象是用户自定义的嘛~) @Nullable public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 虽然它是最重要的方法,但是此处不讲,因为核心原来还是`HandlerMethodArgumentResolver` // 它只是把解析好的放到对应位置里去~~~ // 说明:这里传入了ParameterNameDiscoverer,它是能够获取到形参名的。 // 这就是为何注解里我们不写value值,通过形参名字来匹配也是ok的核心原因~ Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { // trace信息,否则日志也特多了~ logger.trace("Arguments: " + Arrays.toString(args)); } return doInvoke(args); } // doInvoke()方法就不说了,就是个普通的方法调用 // ReflectionUtils.makeAccessible(getBridgedMethod()); // return getBridgedMethod().invoke(getBean(), args); }
对于最后的invoke(),说明一点:这里可是执行的目标方法getBean()哦~~~
这个子类主要提供的能力就是提供了invoke调用目标Bean的目标方法的能力,在这个调用过程中可大有文章可为,当然最为核心的逻辑可是各种各样的HandlerMethodArgumentResolver来完成的,详见下文有分晓。
InvocableHandlerMethod这个子类虽然它提供了调用了能力,但是它却依旧还没有和Servlet的API绑定起来,毕竟使用的是Spring自己通用的的NativeWebRequest,so很容易想到它还有一个子类就是干这事的~
ServletInvocableHandlerMethod
它是对InvocableHandlerMethod的扩展,它增加了返回值和响应状态码的处理,另外在ServletInvocableHandlerMethod有个内部类ConcurrentResultHandlerMethod继承于它,支持异常调用结果处理,Servlet容器下Controller在查找适配器时发起调用的最终就是ServletInvocableHandlerMethod。
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call"); // 处理方法返回值 @Nullable private HandlerMethodReturnValueHandlerComposite returnValueHandlers; // 构造函数略 // 设置处理返回值的HandlerMethodReturnValueHandler public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite returnValueHandlers) { this.returnValueHandlers = returnValueHandlers; } // 它不是复写,但是是对invokeForRequest方法的进一步增强 因为调用目标方法还是靠invokeForRequest // 本处是把方法的返回值拿来进一步处理~~~比如状态码之类的 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 设置HttpServletResponse返回状态码 这里面还是有点意思的 因为@ResponseStatus#code()在父类已经解析了 但是子类才用 setResponseStatus(webRequest); // 重点是这一句话:mavContainer.setRequestHandled(true); 表示该请求已经被处理过了 if (returnValue == null) { // Request的NotModified为true 有@ResponseStatus注解标注 RequestHandled=true 三个条件有一个成立,则设置请求处理完成并返回 if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } // 返回值不为null,@ResponseStatus存在reason 同样设置请求处理完成并返回 } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } // 前边都不成立,则设置RequestHandled=false即请求未完成 // 继续交给HandlerMethodReturnValueHandlerComposite处理 // 可见@ResponseStatus的优先级还是蛮高的~~~~~ mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { // 关于对方法返回值的处理,参见:https://blog.csdn.net/f641385712/article/details/90370542 this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } } // 设置返回的状态码到HttpServletResponse 里面去 private void setResponseStatus(ServletWebRequest webRequest) throws IOException { HttpStatus status = getResponseStatus(); if (status == null) { // 如果调用者没有标注ResponseStatus.code()此注解 此处就忽略它 return; } HttpServletResponse response = webRequest.getResponse(); if (response != null) { String reason = getResponseStatusReason(); // 此处务必注意:若有reason,那就是sendError 哪怕你是200哦~ if (StringUtils.hasText(reason)) { response.sendError(status.value(), reason); } else { response.setStatus(status.value()); } } // 设置到request的属性,把响应码给过去。为了在redirect中使用 // To be picked up by RedirectView webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status); } private boolean isRequestNotModified(ServletWebRequest webRequest) { return webRequest.isNotModified(); } // 这个方法RequestMappingHandlerAdapter里有调用 ServletInvocableHandlerMethod wrapConcurrentResult(Object result) { return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result)); } // 内部类们 private class ConcurrentResultMethodParameter extends HandlerMethodParameter { @Nullable private final Object returnValue; private final ResolvableType returnType; public ConcurrentResultMethodParameter(Object returnValue) { super(-1); this.returnValue = returnValue; // 主要是这个解析 兼容到了泛型类型 比如你的返回值是List<Person> 它也能把你的类型拿出来 this.returnType = (returnValue instanceof ReactiveTypeHandler.CollectedValuesList ? ((ReactiveTypeHandler.CollectedValuesList) returnValue).getReturnType() : ResolvableType.forType(super.getGenericParameterType()).getGeneric()); } // 若返回的是List 这里就是List的类型哦 下面才是返回泛型类型 @Override public Class<?> getParameterType() { if (this.returnValue != null) { return this.returnValue.getClass(); } if (!ResolvableType.NONE.equals(this.returnType)) { return this.returnType.toClass(); } return super.getParameterType(); } // 返回泛型类型 @Override public Type getGenericParameterType() { return this.returnType.getType(); } // 即使实际返回类型为ResponseEntity<Flux<T>>,也要确保对@ResponseBody-style处理从reactive 类型中收集值 // 是对reactive 的一种兼容 @Override public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) { // Ensure @ResponseBody-style handling for values collected from a reactive type // even if actual return type is ResponseEntity<Flux<T>> return (super.hasMethodAnnotation(annotationType) || (annotationType == ResponseBody.class && this.returnValue instanceof ReactiveTypeHandler.CollectedValuesList)); } } // 这个非常有意思 内部类继承了自己(外部类) 进行增强 private class ConcurrentResultHandlerMethod extends ServletInvocableHandlerMethod { // 返回值 private final MethodParameter returnType; // 此构造最终传入的handler是个Callable // result方法返回值 它支持支持异常调用结果处理 public ConcurrentResultHandlerMethod(final Object result, ConcurrentResultMethodParameter returnType) { super((Callable<Object>) () -> { if (result instanceof Exception) { throw (Exception) result; } else if (result instanceof Throwable) { throw new NestedServletException("Async processing failed", (Throwable) result); } return result; }, CALLABLE_METHOD); // 给外部类把值设置上 因为wrapConcurrentResult一般都先调用,是对本类的一个增强 if (ServletInvocableHandlerMethod.this.returnValueHandlers != null) { setHandlerMethodReturnValueHandlers(ServletInvocableHandlerMethod.this.returnValueHandlers); } this.returnType = returnType; } ... } }
andlerMethod用于封装Handler和处理请求的Method;InvocableHandlerMethod增加了方法参数解析和调用方法的能力;ServletInvocableHandlerMethod在此基础上在增加了如下三个能力:
1.对@ResponseStatus注解的支持
1.当一个方法注释了@ResponseStatus后,响应码就是注解上的响应码。 并且,并且如果returnValue=null或者reason不为空(不为null且不为“”),将中断处理直接返回(不再渲染页面)
2.对返回值returnValue的处理
1. 对返回值的处理是使用HandlerMethodReturnValueHandlerComposite完成的
3.对异步处理结果的处理