引入依赖
build.gradle| 1
 | implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
 | 
 自定义Http Client
Apache Http Client
| 1
 | implementation 'io.github.openfeign:feign-httpclient'
 | 
OkHttp
| 1
 | implementation 'io.github.openfeign:feign-okhttp'
 | 
FeignClient接口扫描
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @EnableFeignClients(basePackageClasses = {Application.class, // 本项目根路径
 OtherClient.class  // 其他包的Client
 })
 public class Application {
 public static void main(String[] args) {
 SpringApplication.run(Application.class, args);
 }
 }
 
 | 
注意:使用@ComponentScan无效
Feign接口仅支持继承一个接口
Contract.parseAndValidateMetadata
自定义feign超时
全局默认
| 12
 
 | feign.client.config.default.connect-timeout=10000feign.client.config.default.read-timeout=60000
 
 | 
指定FeignClient的配置
| 12
 
 | feign.client.config.myContextId.connect-timeout=10000feign.client.config.myContextId.read-timeout=60000
 
 | 
Spring构造Feign Client流程
org.springframework.cloud.openfeign.FeignClientFactoryBean#configureFeign
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | protected void configureFeign(FeignContext context, Feign.Builder builder) {FeignClientProperties properties = beanFactory != null ? beanFactory.getBean(FeignClientProperties.class)
 : applicationContext.getBean(FeignClientProperties.class);
 
 FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class);
 setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
 
 if (properties != null && inheritParentContext) {
 if (properties.isDefaultToProperties()) {
 configureUsingConfiguration(context, builder);
 configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
 configureUsingProperties(properties.getConfig().get(contextId), builder);
 }
 else {
 configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
 configureUsingProperties(properties.getConfig().get(contextId), builder);
 configureUsingConfiguration(context, builder);
 }
 }
 else {
 configureUsingConfiguration(context, builder);
 }
 }
 
 | 
问题记录
请求body为空对象时报错
问题复现:
Feign接口定义如下
| 12
 
 | @PostMapping("call")Response call(@RequestBody Request request);
 
 | 
其中 Request 为空对象
| 12
 3
 4
 
 | @Datapublic class Request {
 
 }
 
 | 
调用方执行时会出错
| 1
 | feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type
 | 
解决方法
方案1:调用方增加配置(推荐)
| 1
 | spring.jackson.serialization.fail-on-empty-beans=false
 | 
方案2:body不使用空对象
原理分析
Spring Feign使用HttpMessageConverters处理请求体,最终我们希望是MappingJackson2HttpMessageConverter来序列化我们的body对象,但前提需要通过它的canWrite方法,方法如下
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | @Overridepublic boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
 if (!canWrite(mediaType)) {
 return false;
 }
 if (mediaType != null && mediaType.getCharset() != null) {
 Charset charset = mediaType.getCharset();
 if (!ENCODINGS.containsKey(charset.name())) {
 return false;
 }
 }
 AtomicReference<Throwable> causeRef = new AtomicReference<>();
 if (this.objectMapper.canSerialize(clazz, causeRef)) {
 return true;
 }
 logWarningIfNecessary(clazz, causeRef.get());
 return false;
 }
 
 | 
注意其中的canSerialize方法,当ObjectMapper开启fail-on-empty-beans时(也是默认开启),canSerialize(Object.class)将返回false
所以我们需要关闭Spring管理的ObjectMapper的fail-on-empty-beans选项