1. 问题
spring 是如何把 http 中的 body, 转换为指定类的, 里面的难点其实在于泛型的处理.
2.Spring 的处理
2.1 HandlerMethod
这个类 Spring 对 Method 的封装, 例如使用 @RequestMapping 注解方法, 会使用 HandlerMethod 封装 (其实是其子类 InvocableHandlerMethod). 然后由 InvocableHandlerMethod 对其进行调用
HandlerMethod 的属性如下
- private final Object bean;
- private final BeanFactory beanFactory;
- private final Class<?> beanType;
- private final Method method;
- private final Method bridgedMethod;
- private final MethodParameter[] parameters; // 重点是这个
- private HttpStatus responseStatus;
- private String responseStatusReason;
- private HandlerMethod resolvedFromHandlerMethod;
2.2 如何解析参数的
参考 InvocableHandlerMethod 的 getMethodArgumentValues 的方法, 其中会使用各种 HandlerMethodArgumentResolver 对 Spring mvc 调用参数解析
例如, 路径的中的参数 /path/${var} 使用的 PathVariableMapMethodArgumentResolver 相关注解 @PathVariable
例如, header 中的参数 使用 RequestHeaderMapMethodArgumentResolver 来解析, 相关注解 @RequestHeader
那么 @ResponseBody 使用的 RequestResponseBodyMethodProcessor 来解析的,
2.3 如何把 body 转换为参数类
- @Override
- public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
- NativewebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
- parameter = parameter.nestedIfOptional();
- Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
- ....
- return adaptArgumentIfNecessary(arg, parameter);
- }
其中 readWithMessageConverters 方法是重点, 注意 MethodParameter 其实就是 HandlerMethod 中的属性
继续往里面跳
- GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
- ...
- body = genericConverter.read(targetType, contextClass, inputMessage);
其中上面的那段代码就是读取 http 的 body, 并转换为指定类. 我们就拿常见 Fastjson 的 FastJsonHttpMessageConverter 中的代码来看, 很简单
- public Object read(Type type, //
- Class<?> contextClass, //
- HttpInputMessage inputMessage //
- ) throws IOException, HttpMessageNotReadableException {
- return readType(getType(type, contextClass), inputMessage);
- }
- private Object readType(Type type, HttpInputMessage inputMessage) throws IOException {
- try {
- InputStream in = inputMessage.getBody();
- return JSON.parseObject(in, fastJsonConfig.getCharset(), type, fastJsonConfig.getFeatures());
- } catch (JSONException ex) {
- throw new HttpMessageNotReadableException("JSON parse error:" + ex.getMessage(), ex);
- } catch (IOException ex) {
- throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
- }
- }
3 如何自己实现
通过上面的分析, 如何实现一个简单的数据 mock 回放,(假设的我们 mock 的数据使用 json 来存储的)
- // 第一步: 创建一个 HandlerMethod
- HandlerMethod handlerMethod = new HandlerMethod(bean, method);
- // 第二步: 获取返回类型的 MethodParameter
- MethodParameter methodParameter = handlerMethod.getReturnType().nestedIfOptional();
- // 第三步: 使用 Fastjson 反序列化
- JSONObject.parseObject(phxResult.getVal(), methodParameter.getNestedGenericParameterType());
- // 注, bean 是调用的类的实例, method 是通过反射获取的具体调用方法, 怎么获取不是这里的重点, 省略掉获取
来源: https://www.cnblogs.com/lizo/p/spring.html