重写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修饰的字段才进行下划线转换。

posted @ 2021-11-08 16:25  我没K~  阅读(1141)  评论(0编辑  收藏  举报