重寫Feign編碼器


背景:

  有個spring cloud 架構的項目需要調用php小組的api接口,但php提供的接口入參大部分是下划線命名,而Java這邊的實體類是按照駝峰編寫,如果使用Fegin調用會導致php無法接收參數,例如userId傳過去后,由於php那邊是user_id,這樣php接口無法識別,所以針對這個問題進行了如下特殊處理,主要是通過重寫fegin的默認編碼器實現

 

編碼器原理

  Spring Cloud Feign 的編碼器、解碼器和客戶端都是支持自定義擴展,可以對請求以及結果和發起請求的過程進行自定義實現,Feign 默認encoder實現是SpringEncoder,默認decocer實現是ResponseEntityDecoder,另外還有一些其它的編解碼器。

Encoder/ Decoder 實現 說明
JacksonEncoder,JacksonDecoder 基於 Jackson 格式的持久化轉換協議
GsonEncoder,GsonDecoder 基於Google GSON 格式的持久化轉換協議
SaxEncoder,SaxDecoder 基於XML 格式的Sax 庫持久化轉換協議
JAXBEncoder,JAXBDecoder 基於XML 格式的JAXB 庫持久化轉換協議
ResponseEntityEncoder,ResponseEntityDecoder Spring MVC 基於 ResponseEntity< T > 返回格式的轉換協議
SpringEncoder,SpringDecoder 基於Spring MVC HttpMessageConverters 一套機制實現的轉換協議 ,應用於Spring Cloud 體系中

 

實現方案

 

重寫SpringEncoder

 

**
 * 由於下游接口php是下划線命名,java是駝峰,通過fegin調用會導致下游php接收不到,所以需要針對這種問題特殊處理
 */
public class FeignClientEncoder extends SpringEncoder {

    public static final char UNDERLINE = '_';

    public FeignClientEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        super(messageConverters);
    }

    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
        try {
            super.encode(object, bodyType, template);
            if(ObjectUtils.isNotEmpty(AnnotationUtils.getAnnotation(object.getClass(), FeginCamelCase.class))){
                final String s = JSON.toJSONString(object);
                final Object jsonObject = JSON.parse(s);
                Class clazz = Class.forName(bodyType.getTypeName());
                convertToCamelCase(jsonObject,clazz);
                template.body(JSON.toJSONString(jsonObject));
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public final static void convertToCamelCase(Object json,Class clazz) throws NoSuchFieldException {
        if (json instanceof JSONArray) {
            JSONArray arr = (JSONArray) json;
            for (Object obj : arr) {
                convertToCamelCase(obj,clazz);
            }
        } else if (json instanceof JSONObject) {
            JSONObject jo = (JSONObject) json;
            Set<String> keys = jo.keySet();
            String[] array = keys.toArray(new String[keys.size()]);
            for (String key : array) {
                Field field = clazz.getField(key);
          //由於有些字段因為特殊原因,不想處理成下划線形式,這時候就可以通過注解的方式進行標記不進行處理
                if(ObjectUtils.isEmpty(AnnotationUtils.getAnnotation(field,IgnoreFeginCamelCase.class))){
                    Object value = jo.get(key);
                    final String underLineKey = camelToUnderline(key,1);
                    jo.remove(key);
                    jo.put(underLineKey, value);
                    convertToCamelCase(value,clazz);
                }
            }
        }
    }

    //駝峰轉下划線
    public static String camelToUnderline(String param, Integer charType) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(UNDERLINE);
            }
            if (charType == 2) {
                sb.append(Character.toUpperCase(c));  //統一都轉大寫
            } else {
                sb.append(Character.toLowerCase(c));  //統一都轉小寫
            }
        }
        return sb.toString();
    }

 

啟動類指定編碼器

 

@EnableFeignClients(defaultConfiguration = FeignClientEncoder.class)

 

最后

  指定編碼器后,所有經過Fegin請求都會走這個編碼器,這樣會導致原有的業務受到影響,為了不影響原有的業務,需要通過標識來區分到底什么字段能轉下划線,所以在上面的代碼里,我寫了一個IgnoreFeginCamelCase注解,用於判斷被IgnoreFeginCamelCase修飾的字段才進行下划線轉換。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM