基于@RequestBody注解只能注入對(duì)象和map的解決
@RequestBody注解只能注入對(duì)象和map的問(wèn)題
前后端分離開(kāi)發(fā)模式下,前后端數(shù)據(jù)交互全部采用json,所以在后端在采用spring框架的時(shí)候都會(huì)使用@ResponseBody(后端返回參數(shù)封裝為json格式)和@RequestBody(前端請(qǐng)求攜帶json參數(shù)解析)注解。
但是在實(shí)際開(kāi)發(fā)中,往往@RequestBody的使用會(huì)比較令人難受(超級(jí)難受),因?yàn)樗黶pring官方只支持到將json解析為一個(gè)定義好的對(duì)象或者是一個(gè)通用性的map,而我們實(shí)際項(xiàng)目中經(jīng)常傳遞的參數(shù)僅僅是一個(gè)或者是兩個(gè)參數(shù),這樣的參數(shù)封裝程對(duì)象總是有點(diǎn)大材小用的感覺(jué),并且還消耗性能,而使用map又感覺(jué)操作比較繁瑣,那這樣的話可不可以使用簡(jiǎn)單的類似于@PathVariable(value="")這樣的模式取請(qǐng)求數(shù)據(jù)呢????當(dāng)然可以!!
1、自定義一個(gè)適應(yīng)于這種情況的注解@RequestJson
package cn.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Title: RequestJson
* @date 2018年9月10日
* @version V1.0
* Description: 自定義請(qǐng)求json數(shù)據(jù)解析注解(主要解決單參數(shù)傳遞)
*/
@Target(ElementType.PARAMETER)//使用在參數(shù)上
@Retention(RetentionPolicy.RUNTIME)//運(yùn)行時(shí)注解
public @interface RequestJson {
/**
* 是否必須出現(xiàn)的參數(shù)
*/
boolean required() default true;
/**
* 當(dāng)value的值或者參數(shù)名不匹配時(shí),是否允許解析最外層屬性到該對(duì)象
*/
boolean parseAllFields() default true;
/**
* 解析時(shí)用到的JSON的key
*/
String value() default "";
}
2、自定義RequestJsonHandlerMethodArgumentResolver
實(shí)現(xiàn)HandlerMethodArgumentResolver(spring解析數(shù)據(jù)的接口)
package cn.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cn.annotation.RequestJson;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
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 javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
/**
* Title: RequestJsonHandlerMethodArgumentResolver
* @date 2018年9月10日
* @version V1.0
* Description: 自定義解析json數(shù)據(jù)
*/
public class RequestJsonHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY";
/**
* 設(shè)置支持的方法參數(shù)類型
* @param parameter 方法參數(shù)
* @return 支持的類型
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 支持帶@RequestJson注解的參數(shù)
return parameter.hasParameterAnnotation(RequestJson.class);
}
/**
* 參數(shù)解析,利用fastjson
* 注意:非基本類型返回null會(huì)報(bào)空指針異常,要通過(guò)反射或者JSON工具類創(chuàng)建一個(gè)空對(duì)象
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String jsonBody = getRequestBody(webRequest);
JSONObject jsonObject = JSON.parseObject(jsonBody);
// 根據(jù)@RequestJson注解value作為json解析的key
RequestJson parameterAnnotation = parameter.getParameterAnnotation(RequestJson.class);
//注解的value是JSON的key
String key = parameterAnnotation.value();
Object value = null;
// 如果@RequestJson注解沒(méi)有設(shè)置value,則取參數(shù)名FrameworkServlet作為json解析的key
if (StringUtils.isNotEmpty(key)) {
value = jsonObject.get(key);
// 如果設(shè)置了value但是解析不到,報(bào)錯(cuò)
if (value == null && parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present", key));
}
} else {
// 注解為設(shè)置value則用參數(shù)名當(dāng)做json的key
key = parameter.getParameterName();
value = jsonObject.get(key);
}
Class<?> parameterType = parameter.getParameterType();
// 通過(guò)注解的value或者參數(shù)名解析,能拿到value進(jìn)行解析
if (value != null) {
if (isBasicDataTypes(parameterType)) {
return value;
}
return JSON.parseObject(value.toString(), parameterType);
}
// 解析不到則將整個(gè)json串解析為當(dāng)前參數(shù)類型
if (isBasicDataTypes(parameterType)) {
if (parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present", key));
} else {
return null;
}
}
Object result = parameterType.newInstance();
// 非基本類型,不允許解析所有字段,返回null
if (!parameterAnnotation.parseAllFields()) {
// 如果是必傳參數(shù)拋異常
if (parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present", key));
}
// 否則返回空對(duì)象
return result;
}
// 非基本類型,允許解析,將外層屬性解析
result = JSON.parseObject(jsonObject.toString(), parameterType);
// 如果非必要參數(shù)直接返回,否則如果沒(méi)有一個(gè)屬性有值則報(bào)錯(cuò)
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;
}
}
/**
* 基本數(shù)據(jù)類型直接返回
*/
@SuppressWarnings("rawtypes")
private boolean isBasicDataTypes(Class clazz) {
Set<Class> classSet = new HashSet<>();
classSet.add(String.class);
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(Character.class);
return classSet.contains(clazz);
}
/**
* 獲取請(qǐng)求體JSON字符串
*/
private String getRequestBody(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
// 有就直接獲取
String jsonBody = (String) webRequest.getAttribute(JSONBODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
// 沒(méi)有就從請(qǐng)求中讀取
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;
}
}
3、將上述配置應(yīng)用到spring項(xiàng)目中
重寫(xiě)addArgumentResolvers方法
package cn.config;
import java.nio.charset.Charset;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* Title: WebConfig
* @date 2018年9月10日
* @version V1.0
* Description: 將自定義注解配置到spring
*/
@Configuration
@SuppressWarnings("deprecation")
public class WebConfig extends WebMvcConfigurerAdapter{
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new RequestJsonHandlerMethodArgumentResolver());
}
@Bean
public HttpMessageConverter<String> responseBodyConverter() {
return new StringHttpMessageConverter(Charset.forName("UTF-8"));
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(responseBodyConverter());
}
}
4、配置完成了,簡(jiǎn)單使用
單參數(shù):(不加value默認(rèn)為參數(shù)名)

或者:

多參數(shù):

@RequestBody注解的使用問(wèn)題
今天遇到的問(wèn)題:@RequestBody的使用問(wèn)題
先看一下@RequestBody的作用

我想獲取json字符串某個(gè)字段值,看截圖:

看一下控制臺(tái)的輸出信息:

what ?這什么情況,為什么拿到的是整個(gè)json字符串,然后我繼續(xù)測(cè)試

給了一個(gè)400

what ?這又是什么情況 (好像只能有一個(gè)@RequestBody)我想?yún)?shù)如果是整形的話能不能獲取,我繼續(xù)進(jìn)行測(cè)試代碼:

傳參:

又給了一個(gè)400 (好像只能是String類型) 測(cè)試引用類型對(duì)象

代碼:

傳參:

控制臺(tái)打印:

測(cè)試成功。
個(gè)人總結(jié):
1) 一個(gè)方法只能有一個(gè)@RequestBody
2) 如果接收參數(shù)是字符串類型的,獲取的是整個(gè)json字符串
3) 如果接受的參數(shù)是引用對(duì)象,@requestBody User user 會(huì)將json字符串中的值賦予user中對(duì)應(yīng)的屬性上
需要注意的是,json字符串中key必須和User對(duì)象的屬性名對(duì)應(yīng)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring高階用法之自定義業(yè)務(wù)對(duì)象組件化
這篇文章主要介紹了Spring高階用法之自定義業(yè)務(wù)對(duì)象組件化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Springboot注解之@EnableAutoConfiguration詳解
這篇文章主要介紹了Springboot注解之@EnableAutoConfiguration詳解,@EnableAutoConfiguration是一個(gè)加載Starter目錄包之外的需要Spring自動(dòng)生成bean對(duì)象,本文對(duì)其進(jìn)行總結(jié),需要的朋友可以參考下2023-08-08
Spring計(jì)時(shí)器StopWatch的具體使用
本文主要介紹了Spring計(jì)時(shí)器StopWatch的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
SpringMVC攔截器和異常處理器使用示例超詳細(xì)講解
攔截器(Interceptor)是一種動(dòng)態(tài)攔截方法調(diào)用的機(jī)制,在SpringMVC中動(dòng)態(tài)攔截控制器方法的執(zhí)行。本文將詳細(xì)講講SpringMVC中攔截器參數(shù)及攔截器鏈配置,感興趣的可以嘗試一下2022-09-09
Java 自定義Spring框架與Spring IoC相關(guān)接口分析
Spring框架是由于軟件開(kāi)發(fā)的復(fù)雜性而創(chuàng)建的。Spring使用的是基本的JavaBean來(lái)完成以前只可能由EJB完成的事情。然而,Spring的用途不僅僅限于服務(wù)器端的開(kāi)發(fā)2021-10-10

