背景
- 之前项目使用springboot,接口参数和返回值都使用下划线,项目中实体类都使用的驼峰,所以设置了jackson的统一配置(spring.jackson.property-naming-strategy=SNAKE_CASE),把下划线转换成了驼峰。
- 项目开始使用spring-cloud-feign,对接其它项目,其它项目中的参数和返回值有的使用驼峰,有的使用下划线,所以上述1中的配置不能使用
- 查看@FeignClient的配置可以设置 encoder和decoder,
因此对FeignClient设置了encoder和decoder,
可是日志配置文件中logback.level=debug的时候接口都正常,logback.level=info的时候接口不能正常解析
所以,为了搞清问题出在什么地方,打算撸一遍源码看看什么问题
解决方案
先上解决方案 代码
IOC中FeignClient注册流程:
-
第一步因为需要@EnableFeignClients打开FeignClient的注册,所以查看@EnableFeignClients的源码,发现会import FeignClientsRegistrar类,所以查看FeignClientsRegistrar类
-
第二步 查看FeignClient的注册流程,在.FeignClientsRegistrar 类中可以看到IOC注册的是FeignClientFactoryBean
-
查看FeignClientFactoryBean类,FeignClientFactoryBean实现了FactoryBean接口,因此在IOC容器中 get FeignClient的时候,会调用org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject 方法实例化当前对象,getObject()最终调用org.springframework.cloud.openfeign.Targeter#target 生成返回值
-
查看org.springframework.cloud.openfeign.Targeter 类,发现是一个接口,查看他的实现类,发现有2个,具体用的哪个呢?.
-
查看FeignAutoConfiguration源码,发现如果配置了断路器,用的就是HystrixTargeter,否则就是DefaultTargeter,我们用的是DefaultTargeter
-
进入DefaultTargeter,发现调用了Feign.Builder的target方法
-
进入Feign.Builder,发现调用了feign.Feign.Builder#build 创建ReflectiveFeign对象,再调用feign.ReflectiveFeign#newInstance,终于到了实例列feign对象
-
进入ReflectiveFeign ,查看feign.ReflectiveFeign#newInstance方法,我们feign.ReflectiveFeign#newInstance在发现了生成代理类,进入生成代理类的方法中
-
查看生成的代理类,发现最后返回的是feign.ReflectiveFeign.FeignInvocationHandler,我们知道最终执行的都是代理类中的方法,即java.lang.reflect.InvocationHandler#invoke方法
- 查看代理类的invoke方法,发现是从dispatch中获取方法并执行
- 查看dispatch中获取到的value,发现是feign.SynchronousMethodHandler,现在知道最终执行的是feign.SynchronousMethodHandler#invoke方法
- 还记得刚开始的问题(log.level=debug时,接口正常;log.level=info时接口不正常)吗,知道了最终执行的feign.SynchronousMethodHandler#invoke方法 ,所以在该方法中查找跟log.level相关的代码,打个断点追踪一下,查看调用链路,发现:
判断如果log.level=debug的话,把返回结果拿到,打印日志,然后封装body变成了ByteArrayBody
如果log.level !=debug ,则把原Respone返回,Respone中的body是 InputStream
综合以上两点,得出结论,如果想要自己写@FeignClient中的Decoder,那么需要处理结果从ByteArrayBody获取,和InputStream中获取