原文鏈接:https://blog.csdn.net/xuguofeng2016/article/details/107882619
業務需求
在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要高。
這個類的核心代碼如下:
@Configuration public class FeignClientsConfiguration { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Autowired(required = false) private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>(); @Autowired(required = false) private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>(); @Autowired(required = false) private Logger logger; @Bean @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); } @Bean @ConditionalOnMissingBean @ConditionalOnMissingClass("org.springframework.data.domain.Pageable") public Encoder feignEncoder() { return new SpringEncoder(this.messageConverters); } @Bean @ConditionalOnMissingBean public Contract feignContract(ConversionService feignConversionService) { return new SpringMvcContract(this.parameterProcessors, feignConversionService); } @Bean @Scope("prototype") @ConditionalOnMissingBean public Feign.Builder feignBuilder(Retryer retryer) { return Feign.builder().retryer(retryer); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
Feign請求攔截器
參考FeignClientsConfiguration類,我們可以編寫一個configuration類,注入自定義的RequestInterceptor實現類對象,在apply(RequestTemplate requestTemplate)方法中獲取到請求RestTemplate對象,使用RequestTemplate對象設置請求參數、添加請求頭。
示例如下:
@Configuration public class MyConfig { @Bean("myInterceptor") public RequestInterceptor getRequestInterceptor() { return new MyClientInterceptor(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
MyClientInterceptor類:
class MyClientInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { requestTemplate.query("name", "Allen"); requestTemplate .header("token", "token") .header("id", id); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Feign解碼器
解碼器概述
Feign解碼器負責對響應進行解碼,返回符合Feign接口需求的對象。
我們可以參考FeignClientsConfiguration類中的方式編寫和注入自定義的解碼器。
@Bean @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder( new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); }
- 1
- 2
- 3
- 4
- 5
- 6
繼承SpringDecoder自定義解碼器
class TraceDecoder extends SpringDecoder { TraceDecoder(ObjectFactory<HttpMessageConverters> messageConverters) { super(messageConverters); } @Override public Object decode(Response response, Type type) throws IOException, FeignException { // 這里可以從response對象里面獲取響應頭和響應體 // 獲取響應頭 Map<String, Collection<String>> headers = response.headers(); return super.decode(response, type); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
注入自定義解碼器
feignDecoder()方法完全參考了FeignClientsConfiguration類的寫法。
@Slf4j @Configuration @ConditionalOnClass({Feign.class}) @AutoConfigureBefore(FeignAutoConfiguration.class) public class CommonLogFeignConfig { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; @Bean public Decoder feignDecoder() { return new OptionalDecoder(new ResponseEntityDecoder(new TraceDecoder(this.messageConverters))); } }