在我们的项目中可以看到很多类似的multiRequestBodyDemo(@MultiRequestBody(“dog”) Dog dog, @MultiRequestBody(“user”) User user)的注解,那么这些注解是Spring MVC自带的吗?当然不是,spring MVC中自带的是@RequestBody的注解,这个注解有什么作用呢?这个注解可以将前端传进来的json数据进行解析成json数据。而如果我们没有采样@MultiRequestBody时,通常的做法是将其首先转成json首先转成json,然后进行json数据解析,然后对相关的属性进行逐一获取。下面的例子来源于网上,同时也是我们常用的解析方式之一。但是,如果我们获取属性过多,必然就会带来一个问题,对于代码会显得很长,不够优雅。那还有一种方式那就是采样对象去接收,但是对象接收,但是如果是多个对象呢?那怎么解决这个问题?此时就可以用到multiRequestBodyDemo(@MultiRequestBody(“dog”) Dog dog, @MultiRequestBody(“user”) User user)这种方式进行接收了。但spring boot是不支持这种方式的。因此,就需要自己写一个解析器来解析这样的传入方式和接收的方式。通常,比如我们有分页和对象时,就可以采用这种方式进行接收。
/**
* 修改或者新增热门搜索
* @param hotSearch
* @param request
* @return
*/
@RequestMapping(value = "/servehotselectiveajax")
@ResponseBody
public int servehotselectiveajax(HttpServletRequest req,HttpServletResponse resp,@RequestBody JSONObject obj) {
int count=0;
int countAddHotSearch=0;
int countEditHotSearch=0;
LOGGER.info("data:"+obj.toJSONString());
//data:{"createArr":[{"hotSearchId":"","keyword":"ss","sort":"5","tempid":"21"}],"modifyArr":[{"hotSearchId":"205","keyword":"华为","sort":"2","tempid":"21"},{"hotSearchId":"206","keyword":"游戏本","sort":"3","tempid":"21"},{"hotSearchId":"207","keyword":"平板电视","sort":"3","tempid":"21"},{"hotSearchId":"208","keyword":"连衣裙","sort":"4","tempid":"21"}]}
String data=obj.toJSONString();
//解析json数据
JSONObject json = JSON.parseObject(data);
String createArr=json.getString("createArr");
String modifyArr=json.getString("modifyArr");
if(StringUtils.isNotEmpty(createArr)){
JSONArray createArray=JSONArray.parseArray(createArr);
for(int i=0;i<createArray.size();i++){
Long tempId=JSONObject.parseObject(JSONObject.toJSONString(createArray.get(i))).getLong("tempId");
String keyword=JSONObject.parseObject(JSONObject.toJSONString(createArray.get(i))).getString("keyword");
Integer sort=JSONObject.parseObject(JSONObject.toJSONString(createArray.get(i))).getInteger("sort");
//创建热门搜索对象
HotSearch hotSearch=new HotSearch();
hotSearch.setTempid(tempId);
hotSearch.setKeyword(keyword);
hotSearch.setSort(sort);
hotSearch.setCreateDate(new Date());
hotSearch.setDelFlag("0");
//添加热门搜索信息
countAddHotSearch = hotSearchService.addHotSearchSelective(hotSearch);
}
}
if(StringUtils.isNotEmpty(modifyArr)){
JSONArray modifyArray=JSONArray.parseArray(modifyArr);
for(int i=0;i<modifyArray.size();i++){
Long hotSearchId=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getLong("id");
Long tempId=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getLong("tempId");
String keyword=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getString("keyword");
Integer sort=JSONObject.parseObject(JSONObject.toJSONString(modifyArray.get(i))).getInteger("sort");
//创建热门搜索对象
HotSearch hotSearch=new HotSearch();
hotSearch.setHotSearchId(hotSearchId);
hotSearch.setTempid(tempId);
hotSearch.setKeyword(keyword);
hotSearch.setSort(sort);
//修改热门搜索信息
countEditHotSearch = hotSearchService.modifyHostSearchSelectiveById(hotSearch);
}
}
//判断修改或者新增成功
if(countAddHotSearch>0 || countEditHotSearch>0){
count=1;
}
return count;
}
MultiRequestBody解析器
解决的问题:
1、单个字符串等包装类型都要写一个对象才可以用@RequestBody接收;
2、多个对象需要封装到一个对象里才可以用@RequestBody接收。
主要优势:
1、支持通过注解的value指定JSON的key来解析对象。
2、支持通过注解无value,直接根据参数名来解析对象
3、支持基本类型的注入
4、支持GET和其他请求方式注入
5、支持通过注解无value且参数名不匹配JSON串key时,根据属性解析对象。
6、支持多余属性(不解析、不报错)、支持参数“共用”(不指定value时,参数名不为JSON串的key)
7、支持当value和属性名找不到匹配的key时,对象是否匹配所有属性。
其思路是将前端传入的数据进行获取,也即jsonbody, 获取请求体JSON字符串。获取之后,将其转成jsonObject。获取自定义元注解@MultiRequestBody中的value,如果@MultiRequestBody注解没有设置value,则取参数名FrameworkServlet作为json解析的key。默认是没有的,因此需要获取controller中的注解value值信息作为key,然后通过key拿到属性信息。进行相应的解析。下面是作者 Wangyang Liu的解析具体实现,具体代码可以到github上去获取,https://github.com/chujianyun/Spring-MultiRequestBody
/**
* MultiRequestBody解析器
* 解决的问题:
* 1、单个字符串等包装类型都要写一个对象才可以用@RequestBody接收;
* 2、多个对象需要封装到一个对象里才可以用@RequestBody接收。
* 主要优势:
* 1、支持通过注解的value指定JSON的key来解析对象。
* 2、支持通过注解无value,直接根据参数名来解析对象
* 3、支持基本类型的注入
* 4、支持GET和其他请求方式注入
* 5、支持通过注解无value且参数名不匹配JSON串key时,根据属性解析对象。
* 6、支持多余属性(不解析、不报错)、支持参数“共用”(不指定value时,参数名不为JSON串的key)
* 7、支持当value和属性名找不到匹配的key时,对象是否匹配所有属性。
*
* @author Wangyang Liu QQ: 605283073
* @date 2018/08/27
*/
public class MultiRequestBodyArgumentResolver implements HandlerMethodArgumentResolver {
private static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY";
/**
* 设置支持的方法参数类型
*
* @param parameter 方法参数
* @return 支持的类型
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 支持带@MultiRequestBody注解的参数
return parameter.hasParameterAnnotation(MultiRequestBody.class);
}
/**
* 参数解析,利用fastjson
* 注意:非基本类型返回null会报空指针异常,要通过反射或者JSON工具类创建一个空对象
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String jsonBody = getRequestBody(webRequest);
JSONObject jsonObject = JSON.parseObject(jsonBody);
// 根据@MultiRequestBody注解value作为json解析的key
MultiRequestBody parameterAnnotation = parameter.getParameterAnnotation(MultiRequestBody.class);
//注解的value是JSON的key
String key = parameterAnnotation.value();
Object value;
// 如果@MultiRequestBody注解没有设置value,则取参数名FrameworkServlet作为json解析的key
if (StringUtils.isNotEmpty(key)) {
value = jsonObject.get(key);
// 如果设置了value但是解析不到,报错
if (value == null && parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present", key));
}
} else {
// 注解为设置value则用参数名当做json的key
key = parameter.getParameterName();
value = jsonObject.get(key);
}
// 获取的注解后的类型 Long
Class<?> parameterType = parameter.getParameterType();
// 通过注解的value或者参数名解析,能拿到value进行解析
if (value != null) {
//基本类型
if (parameterType.isPrimitive()) {
return parsePrimitive(parameterType.getName(), value);
}
// 基本类型包装类
if (isBasicDataTypes(parameterType)) {
return parseBasicTypeWrapper(parameterType, value);
// 字符串类型
} else if (parameterType == String.class) {
return value.toString();
}
// 其他复杂对象
return JSON.parseObject(value.toString(), parameterType);
}
// 解析不到则将整个json串解析为当前参数类型
if (isBasicDataTypes(parameterType)) {
if (parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present", key));
} else {
return null;
}
}
// 非基本类型,不允许解析所有字段,必备参数则报错,非必备参数则返回null
if (!parameterAnnotation.parseAllFields()) {
// 如果是必传参数抛异常
if (parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present", key));
}
// 否则返回null
return null;
}
// 非基本类型,允许解析,将外层属性解析
Object result;
try {
result = JSON.parseObject(jsonObject.toString(), parameterType);
} catch (JSONException jsonException) {
// TODO:: 异常处理返回null是否合理?
result = null;
}
// 如果非必要参数直接返回,否则如果没有一个属性有值则报错
if (!parameterAnnotation.required()) {
return result;
} else {
boolean haveValue = false;
Field[] declaredFields = parameterType.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
if (field.get(result) != null) {
haveValue = true;
break;
}
}
if (!haveValue) {
throw new IllegalArgumentException(String.format("required param %s is not present", key));
}
return result;
}
}
/**
* 基本类型解析
*/
private Object parsePrimitive(String parameterTypeName, Object value) {
final String booleanTypeName = "boolean";
if (booleanTypeName.equals(parameterTypeName)) {
return Boolean.valueOf(value.toString());
}
final String intTypeName = "int";
if (intTypeName.equals(parameterTypeName)) {
return Integer.valueOf(value.toString());
}
final String charTypeName = "char";
if (charTypeName.equals(parameterTypeName)) {
return value.toString().charAt(0);
}
final String shortTypeName = "short";
if (shortTypeName.equals(parameterTypeName)) {
return Short.valueOf(value.toString());
}
final String longTypeName = "long";
if (longTypeName.equals(parameterTypeName)) {
return Long.valueOf(value.toString());
}
final String floatTypeName = "float";
if (floatTypeName.equals(parameterTypeName)) {
return Float.valueOf(value.toString());
}
final String doubleTypeName = "double";
if (doubleTypeName.equals(parameterTypeName)) {
return Double.valueOf(value.toString());
}
final String byteTypeName = "byte";
if (byteTypeName.equals(parameterTypeName)) {
return Byte.valueOf(value.toString());
}
return null;
}
/**
* 基本类型包装类解析
*/
private Object parseBasicTypeWrapper(Class<?> parameterType, Object value) {
if (Number.class.isAssignableFrom(parameterType)) {
Number number = (Number) value;
if (parameterType == Integer.class) {
return number.intValue();
} else if (parameterType == Short.class) {
return number.shortValue();
} else if (parameterType == Long.class) {
return number.longValue();
} else if (parameterType == Float.class) {
return number.floatValue();
} else if (parameterType == Double.class) {
return number.doubleValue();
} else if (parameterType == Byte.class) {
return number.byteValue();
}
} else if (parameterType == Boolean.class) {
return value.toString();
} else if (parameterType == Character.class) {
return value.toString().charAt(0);
}
return null;
}
/**
* 判断是否为基本数据类型包装类
*/
private boolean isBasicDataTypes(Class clazz) {
Set<Class> classSet = new HashSet<>();
classSet.add(Integer.class);
classSet.add(Long.class);
classSet.add(Short.class);
classSet.add(Float.class);
classSet.add(Double.class);
classSet.add(Boolean.class);
classSet.add(Byte.class);
classSet.add(Character.class);
return classSet.contains(clazz);
}
/**
* 获取请求体JSON字符串
*/
private String getRequestBody(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
// 有就直接获取
String jsonBody = (String) webRequest.getAttribute(JSONBODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
// 没有就从请求中读取
if (jsonBody == null) {
try {
jsonBody = IOUtils.toString(servletRequest.getReader());
webRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonBody, NativeWebRequest.SCOPE_REQUEST);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return jsonBody;
}
}
是不是很好用!