一、业务需求
在Spring Cloud的项目中,A服务使用Feign调用B服务的某个接口,如果需要传递全局认证token或参数,在方法参数里面加相应字段的方式显然是不可取的。
首先想到的是AOP方式,使用切面拦截Feign方法,在AOP切面里面向方法参数里面添加数据,Feign方法执行完成之后,从响应对象里面获取返回的数据,这样的方式可以解决数据的传递和接收,但也必将需要方法参数和响应对象的支持,与业务耦合,并不是合理的架构实现方案。
如果有某种机制可以拦截到Feign的请求对象和响应对象,便可以获取到请求头和响应头,就可以使用请求头和响应头来传递数据。
经过一番调查,了解到Feign的RequestInterceptor可以拦截到Feign请求,可以获取到请求对象和请求头,但是RequestInterceptor无法处理响应。
于是又进行调查,得知解码器Decoder是对响应进行解码的组件,可以获取到响应对象和响应头。
在调查过程中,还有另外的收获:FeignClientsConfiguration类。
二、FeignClientsConfiguration类
Feign默认配置类是FeignClientsConfiguration类,该类定义了Feign默认的编码器、解码器、所使用的契约等。
Spring Cloud允许通过注解@FeignClient的configuration属性自定义Feign配置,自定义配置的优先级比FeignClientsConfiguration要高。
这个类的核心代码如下:
1 @Configuration 2 public class FeignClientsConfiguration { 3 4 @Autowired 5 private ObjectFactory<HttpMessageConverters> messageConverters; 6 7 @Autowired(required = false) 8 private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>(); 9 10 @Autowired(required = false) 11 private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>(); 12 13 @Autowired(required = false) 14 private Logger logger; 15 16 @Bean 17 @ConditionalOnMissingBean 18 public Decoder feignDecoder() { 19 return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); 20 } 21 22 @Bean 23 @ConditionalOnMissingBean 24 @ConditionalOnMissingClass("org.springframework.data.domain.Pageable") 25 public Encoder feignEncoder() { 26 return new SpringEncoder(this.messageConverters); 27 } 28 29 @Bean 30 @ConditionalOnMissingBean 31 public Contract feignContract(ConversionService feignConversionService) { 32 return new SpringMvcContract(this.parameterProcessors, feignConversionService); 33 } 34 35 @Bean 36 @Scope("prototype") 37 @ConditionalOnMissingBean 38 public Feign.Builder feignBuilder(Retryer retryer) { 39 return Feign.builder().retryer(retryer); 40 } 41 }
三、Feign请求拦截器
参考FeignClientsConfiguration类,我们可以编写一个configuration类,注入自定义的RequestInterceptor实现类对象,在apply(RequestTemplate requestTemplate)方法中获取到请求RestTemplate对象,使用RequestTemplate对象设置请求参数、添加请求头。
示例如下:
1 @Configuration 2 public class MyConfig { 3 @Bean("myInterceptor") 4 public RequestInterceptor getRequestInterceptor() { 5 return new MyClientInterceptor(); 6 } 7 }
MyClientInterceptor类:
1 class MyClientInterceptor implements RequestInterceptor { 2 @Override 3 public void apply(RequestTemplate requestTemplate) { 4 requestTemplate.query("name", "Allen"); 5 requestTemplate 6 .header("token", "token") 7 .header("id", id); 8 } 9 }
四、Feign解码器
1、解码器概述
Feign解码器负责对响应进行解码,返回符合Feign接口需求的对象。
我们可以参考FeignClientsConfiguration类中的方式编写和注入自定义的解码器。
1 @Bean 2 @ConditionalOnMissingBean 3 public Decoder feignDecoder() { 4 return new OptionalDecoder( 5 new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); 6 }
2、继承SpringDecoder自定义解码器
1 class TraceDecoder extends SpringDecoder { 2 3 TraceDecoder(ObjectFactory<HttpMessageConverters> messageConverters) { 4 super(messageConverters); 5 } 6 7 @Override 8 public Object decode(Response response, Type type) throws IOException, FeignException { 9 10 // 这里可以从response对象里面获取响应头和响应体 11 12 // 获取响应头 13 Map<String, Collection<String>> headers = response.headers(); 14 15 return super.decode(response, type); 16 } 17 }
3、注入自定义解码器
feignDecoder()方法完全参考了FeignClientsConfiguration类的写法。
1 @Slf4j 2 @Configuration 3 @ConditionalOnClass({Feign.class}) 4 @AutoConfigureBefore(FeignAutoConfiguration.class) 5 public class CommonLogFeignConfig { 6 7 @Autowired 8 private ObjectFactory<HttpMessageConverters> messageConverters; 9 10 @Bean 11 public Decoder feignDecoder() { 12 return new OptionalDecoder(new ResponseEntityDecoder(new TraceDecoder(this.messageConverters))); 13 } 14 }