feign介面使用JSONObject作為引數遇到的問題
阿新 • • 發佈:2018-11-01
首先有幾個小知識點需要強調一下:
- 當方法的引數是物件時,引數其實是物件引用的拷貝
- 物件的hashCode是兩個物件equals的依據,但hashCode相等不代表他們是同一個物件。
- 驗證兩個物件是否是同一個物件,可以使用“==“。
先上出現問題的程式碼
feign介面
import com.alibaba.fastjson.JSONObject;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation .RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.Map;
import java.util.Set;
@FeignClient("${fxp.my-server}")
public interface IServer {
@RequestMapping(value = "/testjson",method = RequestMethod.POST )
Object testJson(@RequestBody JSONObject params);//注意實現時引數中的@RequestBody不會被實現類繼承
}
實現類
@RestController
public class MyServer implements IServer{
@Override
public Object testJson(@RequestBody JSONObject params) {
JSONObject context = parseContextFromParam(params);
context.put("a" ,"abc");
return params;
}
private JSONObject parseContextFromParam(JSONObject params) {
if (!params.containsKey("context")) {
throw new MyException(
CodeMessageEnum.ILLEGAL_PARAM.getCode(),
CodeMessageEnum.ILLEGAL_PARAM.getMessage(),
CodeMessageEnum.ILLEGAL_PARAM.getHttpStatus()
);
}
return params.getJSONObject("context");
}
}
這是我遇到問題的程式碼(這裡只是為了展示問題,採用最簡化的程式碼),這段程式碼的目的時把傳入的param進行簡單加工(比如本程式碼中的給內層的”context”加一個鍵值對),然後返回給呼叫者。
在本地進行單元測試時是沒有任何問題的,單元測試程式碼:
@AutoWired
private MyServer myServer;
@Test
public void testElements(){
JSONObject param = new JSONObject();
JSONObject context = new JSONObject();
context.put("a","1");
context.put("b","2");
param.put("context",context);
Object result = myServer.testJson(param);
System.out.println(JSONObject.toJSONString(result, SerializerFeature.WriteMapNullValue));//顯示空值鍵值對
}
但是通過feign介面呼叫時就會出現問題,在testJson中不論怎麼修改context都無法改變返回值。
差別在哪裡呢?
feign介面的呼叫實際上是走的http協議,在引數傳遞的時候會經過序列化與反序列化的過程,最後我們在引數裡邊得到的JSONObject的結構可能會不一樣。如圖
在反序列化時spring會使用MappingJackson2HttpMessageConverter實現類(如果沒配置你的引數中對應的converter的話),而Jackson在反序列化的時候會將內層的context解析成LinkedHashMap。
接下來看一下我們使用的fastjson的getJSONObject的實現,如下:
public JSONObject getJSONObject(String key) {
Object value = map.get(key);
if (value instanceof JSONObject) {
return (JSONObject) value;
}
if (value instanceof String) {
return JSON.parseObject((String) value);
}
return (JSONObject) toJSON(value);
}
在toJSON中
如果是LinkedHashMap的話getJSONObject方法會返回一個新的物件。
所以上面的程式碼不管怎麼設定context都不會影響最終的結果。