1. 程式人生 > >feign介面使用JSONObject作為引數遇到的問題

feign介面使用JSONObject作為引數遇到的問題

首先有幾個小知識點需要強調一下:

  • 當方法的引數是物件時,引數其實是物件引用的拷貝
  • 物件的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都不會影響最終的結果。