本类的核心是各式各样的HttpMessageConverter消息转换器,因为最终的write都是交给它们去完成。
此抽象类里,它完成了内容协商~
关于内容协商的详解,强烈建议你点击 这里 。另外 这篇文章也深入的分析了AbstractMessageConverterMethodProcessor这个类,可以作为参考。
既然父类都已经完成了这么多事,那么子类自然就非常的简单的。看看它的两个具体实现子类:
RequestResponseBodyMethodProcessor
顾名思义,它负责处理@RequestBody这个注解的参数
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { parameter = parameter.nestedIfOptional(); // 所以核心逻辑:读取流、消息换换等都在父类里已经完成。子类直接调用就可以拿到转换后的值arg // arg 一般都是个类对象。比如Person实例 Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); // 若是POJO,就是类名首字母小写(并不是形参名) String name = Conventions.getVariableNameForParameter(parameter); // 进行数据校验(之前已经详细分析过,此处一笔带过) if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); if (arg != null) { validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } // 把校验结果放进Model里,方便页面里获取 if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } // 适配:支持到Optional类型的参数 return adaptArgumentIfNecessary(arg, parameter); } }
HttpEntityMethodProcessor
用于处理HttpEntity
和RequestEntity
类型的入参的。
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsParameter(MethodParameter parameter) { return (HttpEntity.class == parameter.getParameterType() || RequestEntity.class == parameter.getParameterType()); } @Override @Nullable public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws IOException, HttpMediaTypeNotSupportedException { ServletServerHttpRequest inputMessage = createInputMessage(webRequest); // 拿到HttpEntity的泛型类型 Type paramType = getHttpEntityType(parameter); if (paramType == null) { // 注意:这个泛型类型是必须指定的,必须的 throw new IllegalArgumentException("HttpEntity parameter '" + parameter.getParameterName() + "' in method " + parameter.getMethod() + " is not parameterized"); } // 调用父类方法拿到body的值(把泛型类型传进去了,所以返回的是个实例) Object body = readWithMessageConverters(webRequest, parameter, paramType); // 注意步操作:new了一个RequestEntity进去,持有实例即可 if (RequestEntity.class == parameter.getParameterType()) { return new RequestEntity<>(body, inputMessage.getHeaders(), inputMessage.getMethod(), inputMessage.getURI()); } else { // 用的父类HttpEntity,那就会丢失掉Method等信息(因此建议入参用RequestEntity类型,更加强大些) return new HttpEntity<>(body, inputMessage.getHeaders()); } } }
注意:这里可没有validate校验了,这也是经常被面试问到的:使用HttpEntity和@RequestBody有什么区别呢?
从代码里可以直观的看到:有了抽象父类后,子类需要做的事情已经很少了,只需要匹配参数类型、做不同的返回而已。
关于它俩的使用案例,此处不用再展示了,因为各位平时工作中都在使用,再熟悉不过了。但针对他俩的使用,我总结出如下几个小细节,供以参考:
- @RequestBody/HttpEntity它的参数(泛型)类型允许是Map
- 方法上的和类上的@ResponseBody都可以被继承,但@RequestBody不可以
- @RequestBody它自带有Bean Validation校验能力(当然需要启用),HttpEntity更加的轻量和方便
HttpEntity/RequestEntity所在包是:org.springframework.http,属于spring-web
@RequestBody位于org.springframework.web.bind.annotation,同样属于spring-web
最后还落了一个ErrorsMethodArgumentResolver,在这里补充一下:
ErrorsMethodArgumentResolver
它用于在方法参数可以写Errors类型,来拿到数据校验结果
public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); return Errors.class.isAssignableFrom(paramType); } @Override @Nullable public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Assert.state(mavContainer != null, "Errors/BindingResult argument only supported on regular handler methods"); ModelMap model = mavContainer.getModel(); String lastKey = CollectionUtils.lastElement(model.keySet()); // 只有@RequestBody/@RequestPart注解的 这里面才会有值 if (lastKey != null && lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) { return model.get(lastKey); } // 简单的说:必须有@RequestBody/@RequestPart这注解标注,Errors参数才有意义 throw new IllegalStateException( "An Errors/BindingResult argument is expected to be declared immediately after " + "the model attribute, the @RequestBody or the @RequestPart arguments " + "to which they apply: " + parameter.getMethod()); } }