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是通過反射獲取的具體調用方法,怎么獲取不是這里的重點,省略掉獲取
