這個問題非常有趣,不是SpringMVC 的問題,是實際開發中混合使用了兩種請求方式暴露出來的,
問題場景
功能模塊中,提供兩個 Http 服務,一個是串列查詢(application/json 請求),一個是串列匯出(表單請求),運行環境發現個問題:MVC model 新添加的屬性,類似的 Http 請求,一個有值,一個沒有
代碼如下:
/**
* application/json 請求, 這種情況 param.field2 有值 ?
* @param param RequestResponseBodyMethodProcessr 處理 HttpServletRequest 引數
*/
@PostMapping(value = "https://www.cnblogs.com/jingdongkeji/archive/2023/06/27/query")
public ResponseResult<Page<SomeData>> queryByCondition(@RequestBody SomeParam param){
// 業務邏輯...
}
/**
* application/x-www-form-urlencoded 請求 這種情況 param.field2 沒有有賦值 ?
* @param param ServletModelAttributeMethodProcessor 處理 HttpServletRequest 引數
*/
@PostMapping(value = "https://www.cnblogs.com/jingdongkeji/archive/2023/06/27/export")
public void exportExcel(SomeParam param) {
// 業務邏輯...
}
public class SomeParam {
// 這個是原有的,有 get set 方法
private String field1;
// 這個是新增的,沒有get set 方法 (這是一個巧合、意外), 問題就出在這里,
private String field2;
}
? 根據代碼分析,那應該是 SpringMVC 針對這兩種引數處理的機制不同,
針對上述的引數處理,可以參考:
RequestResponseBodyMethodProcessor、 ServletModelAttributeMethodProcessor
Insight RequestResponseBodyMethodProcessor
處理 Http Body 的資料,決議注解 RequestBody 的引數,
針對 MimeType 為 application/json 的請求,按照json 格式進行反序列化,
默認引數處理器
MappingJackson2HttpMessageConverter
string 反序列化為物件,使用的是
com.fasterxml.jackson.databind.ObjectMapper,
上述工程中,對 ObjectMapper 開啟 private 屬性檢測,新增的屬性可以正常反序列化,
ObjectMapper mapper = new ObjectMapper();
// 這又是一個巧合、意外 咋還有這個用法
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
Visibility 具體的用法示例參考: Jackson - Decide What Fields Get (De)Serialized | Baeldung
原理: 如果沒有 setter 方法,jackson 會操作 field 來完成賦值,
/**
* This concrete sub-class implements property that is set directly assigning to a Field.
*/
public final static class FieldProperty extends SettableBeanProperty {
@Override
public final void set(Object instance, Object value) throws IOException {
try {
_field.set(instance, value);
} catch (Exception e) {
_throwAsIOE(e, value);
}
}
}
Insight ServletModelAttributeMethodProcessor
自定義 Class 引數決議
通過決議 request parameters, 用來構造和初始化對應的方法入參,
主要通過
ServletRequestDataBinder.bind(request) 來完成,
/**
* Apply given property values to the target object.
* By default, unknown fields will be ignored.
*
* @see org.springframework.validation.DataBinder#applyPropertyValues
*/
protected void applyPropertyValues(MutablePropertyValues mpvs) {
try {
// Bind request parameters onto target object.
// 默認使用 BeanWrapperImpl.setPropertyValue()
getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
}
catch (PropertyBatchUpdateException ex) {
// Use bind error processor to create FieldErrors.
}
}
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException {
// 通過遍歷 request parameters 來嘗試對 target 進行賦值
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ? ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (PropertyValue pv : propertyValues) {
try {
// etPropertyValue 使用 JDK 的 Introspector 來進行序列化操作,
// 沒有setter 方法,自然沒法賦值,
setPropertyValue(pv);
}
}
}
總結
- 一件事情出錯,不是一處問題造成的,
- 工程開發要規范,用最常規、最穩定的辦法來實作,遇到稀奇古怪的問題就是冷門用法帶來的,
- 對于常用的框架和工具庫熟悉其底層原理,遇到問題可以很快定位,
作者:京東物流 楊攀
來源:京東云開發者社區
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/556103.html
標籤:其他
上一篇:std::future 如何保存多個物件進行同步等待
下一篇:返回列表
