如何实现@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
 

 

posted @   atheva  阅读(5096)  评论(0)    收藏  举报
点击右上角即可分享
微信分享提示