如何实现@ResponseBody,把Json字符串转换为指定类型
1.问题
spring 是如何把 http中的body,转换为指定类的,里面的难点其实在于泛型的处理。
2.Spring的处理
2.1 HandlerMethod
这个类Spring对Method的封装,例如使用@RequestMapping注解方法,会使用HandlerMethod封装(其实是其子类InvocableHandlerMethod)。然后由InvocableHandlerMethod对其进行调用
HandlerMethod的属性如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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转换为参数类
1 2 3 4 5 6 7 8 9 10 | @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中的代码来看,很简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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是通过反射获取的具体调用方法,怎么获取不是这里的重点,省略掉获取
1 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步