前言:
spring boot 默认的参数处理,不能支持泛型的处理,例如:
后端代码:
@RequestMapping("entityParamTest")
@ResponseBody
public String entityParamTest(UserVO<DepartmentVO> userVO){
System.out.println(userVO);
return userVO.toString();
}
import lombok.Data;
/**
* @author phacxj
*/
@Data
public class UserVO<T> {
private T data;
private String username ;
private String realName ;
private Integer age;
private Boolean isShow;
private String [] phones;
private DepartmentVO departmentVO;
}
import lombok.Data;
/**
* @author phacxj
*/
@Data
public class DepartmentVO {
private String name ;
private String code ;
}
前端代码:
$.ajax({
url : "entityParamTest?data.name=啊啊&username=安安&departmentVO.code=哈哈",
type: "post",
});
控制台输出为:
UserVO(data=java.lang.Object@70cd5f86, username=安安, realName=null, age=null, isShow=null, phones=null, departmentVO=DepartmentVO(name=null, code=哈哈))
其中data是DepartmentVO,但是返回的是Obejct 是因为,spring boot原生的不支持泛型的处理。
@RequestBody方式处理泛型
@RequestBody参数注解是支持泛型的,但前提是ajax的请求必须是
contentType :“application/json”,
data : JSON.stringify(data),
前端代码:
//@RequestBody处理,必须是JSON格式,且是JSON字符串 contentType :"application/json",data : JSON.stringify(data),
let data = {
data : {
name : "啊啊"
},
departmentVO : {
code : "哈哈"
}
};
$.ajax({
url : url,
type: "post",
contentType :"application/json",
data : JSON.stringify(data),
});
后端代码:
@RequestMapping("entityParamTest")
@ResponseBody
public String entityParamTest(@RequestBody UserVO<DepartmentVO> userVO){
System.out.println(userVO);
return userVO.toString();
}
控制台输出为:
UserVO(data=DepartmentVO(name=啊啊, code=null), username=null, realName=null, age=null, isShow=null, phones=null, departmentVO=DepartmentVO(name=null, code=哈哈))
这里可以看到数据是可以输出来的。但是这种情况不支持
自定义参数注解处理泛型的问题和带点参数名的问题
1.定义自定义注解
后端代码:
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface EntityParam {
}
2.定义自定义参数解析器(最核心的步骤)
/**
* @author phacxj
*/
@Configuration
public class EntityParamHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final String POINT = ".";
private static final String LEFT_PARENTHESIS = "[";
private static final String RIGHT_PARENTHESIS = "]";
private static final String PARENTHESIS = "[]";
private static final String CONTENT_TYPE = "application/json";
private final Logger logger = LoggerFactory.getLogger(EntityParamHandlerMethodArgumentResolver.class);
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(EntityParam.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory){
//获取JSON字符串
String jsonStr;
//判断content-type是否是application/json 的数据类型
String contentType = nativeWebRequest.getHeader("content-type");
if (StringUtils.isNotBlank(contentType) && contentType.contains(CONTENT_TYPE)){
jsonStr = getJSONParam(nativeWebRequest);
}else {
jsonStr = parseParamToJSONStr(nativeWebRequest);
}
try {
return JSON.parseObject(jsonStr, methodParameter.getGenericParameterType());
}catch (JSONException e){
logger.error("resolveArgument中JSON.parseObject方法出错:"+e.getMessage(),e);
return null;
}
}
/**
*@Description 获取JSON字符串中的数据
*@Author phacxj
*@Createtime 2019/2/12 16:12
*@Param [request]
*@Return java.lang.String
*/
private String getJSONParam(NativeWebRequest request){
StringBuilder stringBuilder = new StringBuilder();
try {
//1.获取输入流
BufferedReader stramReader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(request.getNativeRequest(HttpServletRequest.class)).getInputStream()));
//2.写入数据到StringBuilder
String line;
while ((line = stramReader.readLine()) != null){
stringBuilder.append(line);
}
}catch (Exception e){
logger.error("获取JSON字符串中的数据错误:"+e.getMessage(),e);
}
return stringBuilder.toString();
}
/**
*@Description 将user.id=1这种形式的数据转为{user:{id:1}}形式
*@Author phacxj
*@Createtime 2019/2/13 9:42
*@Param [request]
*@Return java.lang.String
*/
private String parseParamToJSONStr(NativeWebRequest request){
Map<String,String [] > conditionMap = request.getParameterMap();
JSONObject jsonObject = new JSONObject();
Iterator<String> names = request.getParameterNames();
while (names.hasNext()){
String name = names.next();
String [] value = conditionMap.get(name);
if (value != null){
parseHavePointParamToJSON(jsonObject,name,value);
}
}
return jsonObject.toJSONString();
}
/**
*@Description 处理带有点的参数
*@Author phacxj
*@Createtime 2019/2/13 10:12
*@Param [jsonObject, name, value]
*@Return void
*/
private void parseHavePointParamToJSON(JSONObject jsonObject,String name,String [] value){
//处理中括号
if (name.contains(LEFT_PARENTHESIS)){
name = name
.replace(PARENTHESIS,"")
.replace(LEFT_PARENTHESIS,POINT)
.replace(RIGHT_PARENTHESIS,"");
}
if (!name.contains(POINT)){
//如果没有小数点,证明已经是最后一个了
//判断是否是已经设置过值
if (value.length>1){
//如果是组数,则以数组的形式存放数据
JSONArray jsonArray = new JSONArray();
jsonArray.addAll(Arrays.asList(value));
jsonObject.put(name,jsonArray);
}else {
jsonObject.put(name,value[0]);
}
}else {
//如果还有小数点
int pointIndex = name.indexOf(POINT);
String tempName = name.substring(0,pointIndex);
String subName = name.substring(name.indexOf(POINT)+1,name.length());
JSONObject subJSONObject;
//判断是否已经有对应的属性了
if (jsonObject.getJSONObject(tempName) == null){
//如果没有
subJSONObject = new JSONObject();
}else {
//如果有
subJSONObject = jsonObject.getJSONObject(tempName);
}
jsonObject.put(tempName,subJSONObject);
//递归处理
parseHavePointParamToJSON(subJSONObject,subName,value);
}
}
}
3.拦截器配置
注意:这里是spring boot 2.0以上的版本,定义了拦截器之后,默认的静态资源目录会被改变,所以需要
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
/**
* @author phacxj
*/
@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurationSupport {
/**
* @Description 注册到spring容器中
* @Author phacxj
* @Createtime 2019/1/31 11:03
* @Param
* @Return
*/
@Resource
private EntityParamHandlerMethodArgumentResolver handlerParameterResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(handlerParameterResolver);
super.addArgumentResolvers(argumentResolvers);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
测试:
带有.的参数名称泛型处理(例如:user.name=111)
后端代码:
@RequestMapping("entityParamTest")
@ResponseBody
public String entityParamTest(@EntityParam UserVO<DepartmentVO> userVO){
System.out.println(userVO);
return userVO.toString();
}
前端代码:
let url = "entityParamTest?data.name=啊啊&username=安安&departmentVO.code=哈哈";
$.ajax({
url : url,
type: "post",
});
控制台输出:
UserVO(data=DepartmentVO(name=啊啊, code=null), username=null, realName=null, age=null, isShow=null, phones=null, departmentVO=DepartmentVO(name=null, code=哈哈))
可以看到,泛型的问题解决了。
优化@RequestBody 的ajax请求必须是JSON字符串请求的问题测试:
后端代码:
@RequestMapping("entityParamTest")
@ResponseBody
public String entityParamTest(@EntityParam UserVO<DepartmentVO> userVO){
System.out.println(userVO);
return userVO.toString();
}
前端代码:
let data = {
data : {
name : "啊啊"
},
departmentVO : {
code : "哈哈"
}
};
$.ajax({
url : "entityParamTest",
type : "post",
data : data
})
控制台输出:
UserVO(data=DepartmentVO(name=啊啊, code=null), username=null, realName=null, age=null, isShow=null, phones=null, departmentVO=DepartmentVO(name=null, code=哈哈))
可以看到,没有:
contentType :“application/json”,
data : JSON.stringify(data),
依然可以接受到数据
注意:这种方式,只能使用与JSONObject的情况,如果要传递JSONArray的数据,还是需要指定
contentType :“application/json”,
data : JSON.stringify(data),
总结
这个参数注解的作用有:
1.替代@RequestBody
2.处理带有点(user.name)且是泛型的参数问题
3.优化@RequestBody中前端必须是以JSON字符串方式传递参数的问题