先引入一篇博客: https://www.cnblogs.com/java-zhao/p/9119258.html
关于处理使用@requstBody注解后自定义参数解析器,失败,原因是spring会使用自己默认的参数解析器来处理参数解析,上面这篇博客涉及了参数解析器的顺序
因此大部门都是采用自定义注解来实现的
我的业务场景:
对接第三方平台,别人调用我的接口,但是接口文档参数已经定好位有下划线的参数,觉得不规范,但是别人又不好修改,采用自定义注解+参数解析器来讲下划线的参数名称,转为驼峰命名参数接收
RequestMappingHandlerAdapter 中查看默认的参数解析器
通用自定义参数解析类,实现 HandlerMethodArgumentResolver 接口,同时交给spring回调,来实现目的
package com.wm.resolverstudy1.config;
import com.alibaba.fastjson.JSONObject;
import com.wm.resolverstudy1.annotation.RequestPropertiesBody;
import org.springframework.core.MethodParameter;
import org.springframework.objenesis.instantiator.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import sun.reflect.misc.FieldUtil;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @author 半卷流年
* @date 2020-8-13 10:28
*/
public class RequestModelArgumentResolver implements HandlerMethodArgumentResolver {
private final Pattern underLinePattern = Pattern.compile("_(\\w)");
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestPropertiesBody.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Class> parameterType = parameter.getParameterType();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
CustomHttpServletRequestWrapper requestWrapper = new CustomHttpServletRequestWrapper(request);
String body = requestWrapper.getBody();
//实例化对象
Object result = ClassUtils.newInstance(parameterType);
if(StringUtils.isEmpty(body)){
return result;
}
JSONObject jsonObject = JSONObject.parseObject(body);
//获取字段
Field[] fields = FieldUtil.getDeclaredFields(parameterType);
Map fieldMap = Arrays.stream(fields).collect(Collectors.toMap(Field::getName, v -> v));
Object arg = null;
//往对象写入值
for (Map.Entry entry : jsonObject.entrySet()) {
String fieldName = entry.getKey();
//处理下划线转驼峰命名
if(isExistUnderLine(fieldName)){
fieldName = this.underLineToCamel(fieldName);
}
Field field = fieldMap.getOrDefault(fieldName, null);
if(ObjectUtils.isEmpty(field)){
continue;
}
arg = entry.getValue();
if(ObjectUtils.isEmpty(arg)){
continue;
}
if (isPrimitive(field.getType())) {
Class> parType = field.getType();
arg = getArg(parType, arg);
}
Method setter = getSetterMethod(parameterType, field,fieldName);
if (setter != null) {
setter.invoke(result, arg);
}
}
return result;
}
private boolean isExistUnderLine(String value){
Matcher m = this.underLinePattern.matcher(value);
return m.find();
}
private String underLineToCamel(final String value) {
final StringBuffer sb = new StringBuffer();
Matcher m = this.underLinePattern.matcher(value);
while (m.find()){
m.appendReplacement(sb, m.group(1).toUpperCase());
}
m.appendTail(sb);
return sb.toString();
}
private Method getSetterMethod(Class> clazz, Field field,String fileName) throws NoSuchMethodException {
return clazz.getDeclaredMethod("set" + toUpperCaseFirstOne(fileName), field.getType());
}
private boolean isPrimitive(Class> clazz) {
return Integer.class.equals(clazz) ||
Double.class.equals(clazz) ||
Float.class.equals(clazz) ||
Boolean.class.equals(clazz) ||
Long.class.equals(clazz) ||
Byte.class.equals(clazz) ||
Short.class.equals(clazz) ||
clazz.isPrimitive();
}
private Object getArg(Class> primitiveClass, Object value) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object result = null;
if (isPrimitive(primitiveClass)) {
String primitiveName = primitiveClass.getSimpleName();
if ("char".equals(primitiveName)) {
return value;
}
String firstUpperprimitiveName = toUpperCaseFirstOne(primitiveName);
Class> encapsulationClass = "int".equals(primitiveName) ? Class.forName("java.lang." + firstUpperprimitiveName + "eger") : Class.forName("java.lang." + firstUpperprimitiveName);
result = getParseMethod(encapsulationClass, firstUpperprimitiveName).invoke(null, value);
}
return result;
}
private Method getParseMethod(Class> encapsulationClass, String firstUpperprimitiveName) throws NoSuchMethodException {
String methodName = "parse" + firstUpperprimitiveName;
return encapsulationClass.getDeclaredMethod(methodName, String.class);
}
private String toUpperCaseFirstOne(String fieldName) {
if (Character.isUpperCase(fieldName.charAt(0))){
return fieldName;
}
else {
return String.valueOf(Character.toUpperCase(fieldName.charAt(0))) + fieldName.substring(1);
}
}
}
用到了,获取传入的json字符串,重写获取request中的数据,因为request使用一次就会关闭流的
package com.wm.resolverstudy1.config;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* @author 半卷流年
* @date 2020-8-13 16:17
*/
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final String body;
public String getBody() {
return this.body;
}
public CustomHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
注入的spring中
package com.wm.resolverstudy1.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @author 半卷流年
* @date 2020-8-13 14:20
*/
@Configuration
public class AppWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List resolvers) {
resolvers.add(0,new RequestModelArgumentResolver());
}
}
用到的实体类和自定义注解:
package com.wm.resolverstudy1.annotation;
import java.lang.annotation.*;
/**
* @author 半卷流年
* @date 2020-8-13 14:13
* 模仿spring requestBody注解
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestPropertiesBody {
boolean required() default true;
}
package com.wm.resolverstudy1.model;
import lombok.Data;
import java.io.Serializable;
/**
* @author 半卷流年
* @date 2020-8-13 10:25
*/
@Data
public class User implements Serializable {
private static final long serialVersionUID = -7551221166493676100L;
private String nameName;
private String name;
}
测试:
package com.wm.resolverstudy1.controller;
import com.wm.resolverstudy1.annotation.RequestPropertiesBody;
import com.wm.resolverstudy1.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* @author 半卷流年
* @date 2020-8-13 10:26
*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Autowired
private RequestMappingHandlerAdapter adapter;
@PostMapping("/test4")
public String test4(@RequestPropertiesBody User user){
log.info("请求数据:{}",user);
//return user.getNameName();
return "ok";
}
}