- SpringBoot2.x之HandlerMethodArgum
- SpringBoot2.x之HandlerMethodArgum
- SpringBoot2.x之HandlerMethodArgum
- SpringMVC常用接口之HandlerMethodArgum
- SpringBoot2.x—SpringCache(4)Spri
- SpringBoot2.x过滤器OncePerRequestFi
- SpringBoot2.x【八】集成Redis缓存
- 5.3-SpringBoot2.x实战整合Redis客户端+单元
- 5.2-新版SpringBoot2.5项目创建—小滴课堂学习笔记
- Spring Boot 2.0 常见问题总结(一)
你还在为定义Vo对象而烦恼吗?可以使用自定义注解去获取Json的值,如同@RequestParam那样。
1. 自定义注解
/**
* 自定义注解,将JSON解析为对象。
* 对没错就是抄袭的@RequestParam
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonParam {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the request parameter to bind to.
* @since 4.2
*/
@AliasFor("value")
String name() default "";
/**
* Whether the parameter is required.
* <p>Defaults to {@code true}, leading to an exception being thrown
* if the parameter is missing in the request. Switch this to
* {@code false} if you prefer a {@code null} value if the parameter is
* not present in the request.
* <p>Alternatively, provide a {@link #defaultValue}, which implicitly
* sets this flag to {@code false}.
*/
boolean required() default true;
/**
* The default value to use as a fallback when the request parameter is
* not provided or has an empty value.
* <p>Supplying a default value implicitly sets {@link #required} to
* {@code false}.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
2. 自定义解析器
2.1 自定义解析器顺序
@Slf4j
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@PostConstruct
public void init() {
//获取到自定义requestMappingHandlerAdapter的属性(只读)
List<HandlerMethodArgumentResolver> resolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> newResolvers =
new ArrayList<>(resolvers.size() + 2);
// 添加 解析器 到集合首位
newResolvers.add(new JsonParamAnnotationResolver());
// 添加 已注册的 Resolver 对象集合
newResolvers.addAll(resolvers);
// 从新设置 Resolver 对象集合
requestMappingHandlerAdapter.setArgumentResolvers(newResolvers);
}
}
2.2 实现自定义解析器
其实就是参考的@RequestBody注解来实现,@JsonParam可以看着为轻量级的获取Json参数的自定义实现注解。
@Slf4j
public class JsonParamAnnotationResolver implements HandlerMethodArgumentResolver {
private static ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(JsonParam.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
JsonParam jsonParam = parameter.getParameterAnnotation(JsonParam.class);
boolean required = jsonParam.required();
Class<?> type = parameter.getParameterType();
String paramName = jsonParam.value();
Object value;
try {
//拿到参数。读取请求对象的流
Object param = RequestBodyScope.get(paramName);
// 传入type和contextClass对象,得到JavaType
//org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.getJavaType 源码。
JavaType javaType =
getJavaType(parameter.getGenericParameterType(), parameter.getContainingClass());
//Jackson+JavaType下支持泛型的返回。
value = value(param, javaType);
} catch (Exception e) {
//出现异常,则抛出参数非法的异常
throw new IllegalArgumentException(e);
}
if (value != null) {
return value;
}
if (required) {
throw new MissingServletRequestParameterException(paramName, type.getTypeName());
}
if (!Objects.equals(jsonParam.defaultValue(), ValueConstants.DEFAULT_NONE)) {
value = jsonParam.defaultValue();
}
return value;
}
/**
* 将{@link java.lang.reflect.Type} 转化为Jackson需要的{com.fasterxml.jackson.databind.JavaType}
*/
public static JavaType getJavaType(Type type, Class<?> contextClass) {
//MAPPER这个可以使用ObjectMapperUtils中ObjectMapper
TypeFactory typeFactory = objectMapper.getTypeFactory();
//这种是处理public <T extends User> T testEnvV3(@JsonParam("users") List<T> user) 这种类型。
return typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));
}
/**
* 将Object对象转换为具体的对象类型(支持泛型)
*/
public static <T> T value(Object rawValue, JavaType javaType) {
return objectMapper.convertValue(rawValue, javaType);
}
}
2.3 一些工具类
因为流只能读取一次,所以需要在第一次读取的时候,将解析的对象存入ThreadLocal中,以便于多次使用@JsonParam来进行解析。
@Slf4j
public class RequestBodyScope {
/**
* 需要将其设置到ThreadLocal中,以便多个@JsonParam注解多次进行解析。
* 注意内存泄露的问题。
*/
private static ThreadLocal<Map<String, Object>> REQUEST_BODY_THREAD_LOCAL =
ThreadLocal.withInitial(RequestBodyScope::resolveRequestBody);
private static ObjectMapper MAPPER = new ObjectMapper();
/**
* 获取RequestBody里面的值
*/
public static Object get(String name) {
return REQUEST_BODY_THREAD_LOCAL.get().get(name);
}
/**
* 回收资源
*/
public static void clear() {
REQUEST_BODY_THREAD_LOCAL.remove();
}
/**
* 解析requestBody对象为Map对象
*/
private static Map<String, Object> resolveRequestBody() {
HttpServletRequest request = ((ServletRequestAttributes) currentRequestAttributes()).getRequest();
if (!isJsonRequest(request)) {
return Collections.emptyMap();
}
try (InputStream input = request.getInputStream()) {
byte[] bytes = IOUtils.toByteArray(input);
String encoding = StringUtils.defaultIfBlank(request.getCharacterEncoding(), "UTF-8");
String content = new String(bytes, encoding);
return fromJson(content);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 将json串转化为Map对象
*/
private static Map<String, Object> fromJson(String json) {
if (StringUtils.isEmpty(json)) {
json = "{}";
}
try {
//json串转化为特定格式的Map对象
return MAPPER.readValue(json,
defaultInstance().constructMapType(Map.class, String.class, Object.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 判断是否为JSON格式?
*/
private static boolean isJsonRequest(HttpServletRequest request) {
String contentType = request.getHeader("Content-Type");
return contentType != null && contentType.toLowerCase().contains("application/json");
}
}
3. 如何测试
@Slf4j
@RestController
public class ResolverController {
@RequestMapping(value = "/r/t1", method = RequestMethod.POST)
public String tt(@JsonParam("name") String name, @JsonParam("pid") Long id) {
log.info("输出:{},{}",name,id);
return "success";
}
}
相关文章
SpringBoot2.x之HandlerMethodArgumentResolver实战
SpringBoot2.x之HandlerMethodArgumentResolver(2)—自定义解析器顺序
网友评论