Feign详解5-springcloud OpenFeign详解

╰+攻爆jí腚メ 2022-11-21 04:29 642阅读 0赞
  1. 前面我们分析 Feign 参数解析的整个流程,Feign 原生已经支持 FeignJAX-RS 1/2 声明式规范, springCloud feign主要对netflix feign进行了增强和包装, 使之支持Spring MVC.
  2. 在前面的分析中,我们只是创建了一个客户端去联接一个服务.
  3. GitHub github = Feign.builder()
  4. .encoder(new JacksonEncoder())
  5. .decoder(new JacksonDecoder())
  6. .options(new Request.Options(10000,10000,true))
  7. .target(GitHub.class, "https://api.github.com");
  8. GitHub github = HystrixFeign.builder()
  9. //.client(new OkHttpClient())
  10. //.decoder(new GsonDecoder())
  11. //.contract(new JAXRSContract())
  12. //.options( new Request.Options(5000,8500)) //options方法指定连接超时时长及响应超时时长
  13. // .retryer( new Retryer.Default(5000,5000,3)) // retryer方法主要是指定重试策略
  14. .target(GitHub.class, "https://api.github.com");

其中值得注意的点有:

  1. 1. 客户端类型有: Feign, HystrixFeign .
  2. 2.再调用客户端的builder()方法生成对应的Builder对象.
  3. 3. Builder对象中注入 encoder,decoder,options,retryer,logLevel,logger, Contract等对象.
  4. 4. 调用buildertarget方法生成代理对象.

那么在分析Spring Cloud OpenFeign的源码时,因为它采用spring boot 的自动装配方案,所以我们要注意这些对象是如何自动装配进来的。

首先我们看一个简单的使用案例.

1. Spring Cloud OpenFeign 最简使用

1.1 引入 maven

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-openfeign</artifactId>
  4. </dependency>

1.2 @EnableFeignClients 注解扫描包

  1. @SpringBootApplication
  2. @EnableFeignClients // 默认扫描 FeignApplication.class 包下 @FeignClient 注解
  3. public class FeignApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(FeignApplication.class);
  6. }
  7. }

1.3 @FeignClient 配置

  1. @FeignClient(name="MICROSERVICE-PROVIDER-PRODUCT",
  2. configuration = FeignClientConfig.class,
  3. fallbackFactory = IProductClientServiceFallbackFactory.class) // 配置要按自定义的类FeignClientConfig
  4. public interface IProductClientService {
  5. @RequestMapping("/product/get/{id}") //这个springMVC的调用地址要与服务提供方要求的完全一样
  6. public Product getProduct(@PathVariable("id")long id);
  7. @RequestMapping("/product/list")
  8. public List<Product> listProduct() ;
  9. @RequestMapping("/product/add")
  10. public boolean addPorduct(Product product) ;
  11. }

总结: 至此,可以像使用普通接口一样调用 http 了. 注意 这个IProductClientService是使用SpringMVC的注解以提供服务的,所以对应的Feign中的Contract应用支持SpringMvc的方案,而不是前面的案例中用过的JAXRSContract, BaseContract了.

2. Feign 整体装配流程分析

注意: OpenFeign 装配有两个入口:

  1. springboot的 EnableAutoConfiguration 自动装配(spring.factories)

    1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    2. org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
    3. org.springframework.cloud.openfeign.FeignAutoConfiguration,\
    4. org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
    5. org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration

    watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70

    • FeignAutoConfiguration 自动装配 FeignContext 和 Targeter,以及 Client 配置。

      • FeignContext 是每个 FeignClient 的装配上下文,默认的配置是 FeignClientsConfiguration
      • Targeter 有两种实现:一是 DefaultTargeter,直接调用 Feign.Builder; 二是 HystrixTargeter,调用 HystrixFeign.Builder,开启熔断。
      • Client :自动装配 ApacheHttpClient,OkHttpClient,装配条件不满足时,默认就是 Client.Default。但这些 Client 都没有实现负载均衡。
    • FeignRibbonClientAutoConfiguration 实现负载均衡,负载均衡是在 Client 这一层实现的。

      • HttpClientFeignLoadBalancedConfiguration ApacheHttpClient 实现负载均衡
      • OkHttpFeignLoadBalancedConfiguration OkHttpClient实现负载均衡
      • DefaultFeignLoadBalancedConfiguration Client.Default实现负载均衡
    • org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration: 这两个自动化装配类是关于 请求和响应的压缩配置的.
  2. @EnableFeignClients 自动扫描

    1. @EnableFeignClients("com.yc.springcloud2.service")注入 FeignClientsRegistrarFeignClientsRegistrar 开启自动扫描,将@EnableFeignClients配置的包下用 @FeignClient 标注的接口包装成 FeignClientFactoryBean 对象,最终通过 Feign.Builder 生成该接口的代理对象。而 Feign.Builder 的默认配置是 FeignClientsConfiguration,是在 FeignAutoConfiguration 自动注入的。

注意: 熔断与限流是 FeignAutoConfiguration 通过注入 HystrixTargeter 完成的,而负载均衡是 FeignRibbonClientAutoConfiguration 注入的。

3. Feign 自动装配

3.1 FeignAutoConfiguration类分析

3.1.1 FeignContext

  1. // FeignAutoConfiguration 自动装配 FeignContext
  2. @Autowired(required = false)
  3. private List<FeignClientSpecification> configurations = new ArrayList<>();
  4. @Bean
  5. public FeignContext feignContext() {
  6. FeignContext context = new FeignContext();
  7. context.setConfigurations(this.configurations);
  8. return context;
  9. }
  10. FeignContext 是每个 Feign 客户端配置的上下文环境,如果我们调用一个服务的话,比如ServiceA,那么这个服务就会关联一个spring容器,FeignContext就代表一个独立的容器,关联着自己独立的一些组件,例如Logger组件、Decoder组件、Encoder组件等等。

注意: 每个 Feign 客户端除了默认 FeignClientsConfiguration,还可以自定义配置类 FeignClientSpecification,这些配置是如何注入的,会在 @EnableFeignClients 和 @FeignClient 源码分析时具体说明。

  1. public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
  2. public FeignContext() {
  3. // FeignClientsConfiguration中会加载Encoder、Decoder、Logger等组件
  4. super(FeignClientsConfiguration.class, "feign", "feign.client.name");
  5. }
  6. }

FeignClientsConfiguration 是 Feign 的默认配置,可以通过 @EnableFeignClients 和 @FeignClient 修改默认配置(在程序代码中自定义的配置项优先于默认配置)。

FeignClientsConfiguration 主要配置如下:

  1. @Configuration
  2. public class FeignClientsConfiguration {
  3. //解码器
  4. @Bean
  5. @ConditionalOnMissingBean
  6. public Decoder feignDecoder() {
  7. return new OptionalDecoder(
  8. new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
  9. }
  10. //编码器
  11. @Bean
  12. @ConditionalOnMissingBean
  13. @ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
  14. public Encoder feignEncoder() {
  15. return new SpringEncoder(this.messageConverters);
  16. }
  17. // Contract 适配 Spring MVC 注解
  18. @Bean
  19. @ConditionalOnMissingBean
  20. public Contract feignContract(ConversionService feignConversionService) {
  21. return new SpringMvcContract(this.parameterProcessors, feignConversionService);
  22. }
  23. //重试器
  24. @Bean
  25. @ConditionalOnMissingBean
  26. public Retryer feignRetryer() {
  27. return Retryer.NEVER_RETRY;
  28. }
  29. // 一般环境都会配置feign.hystrix.enabled = true,这里直接看HystrixFeign.builder();
  30. @Configuration
  31. @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
  32. protected static class HystrixFeignConfiguration {
  33. @Bean
  34. @Scope("prototype")
  35. @ConditionalOnMissingBean
  36. @ConditionalOnProperty(name = "feign.hystrix.enabled")
  37. public Feign.Builder feignHystrixBuilder() {
  38. return HystrixFeign.builder();
  39. }
  40. }
  41. ...
  42. }

在FeignClientsConfiguration类中我们已经可以看到 feign客户端builder()中要装配的对象( decoder, encoder, logger, retryer,contract ) 都准备好了.

3.1.2 Targeter: Targeter 有两种实现

再接着阅读 FeignAutoConfiguration 类中的 Targeter 有两种实现:一是 DefaultTargeter,直接调用 Feign.Builder; 二是 HystrixTargeter,调用 HystrixFeign.Builder,开启熔断。

  1. // 如果配置了feign.hystrix.HystrixFeign 则创建HystrixTargeter
  2. @Configuration
  3. @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
  4. protected static class HystrixFeignTargeterConfiguration {
  5. @Bean
  6. @ConditionalOnMissingBean
  7. public Targeter feignTargeter() {
  8. return new HystrixTargeter();
  9. }
  10. }
  11. // 如果没有配置feign.hystrix.HystrixFeign 则创建DefaultTargeter
  12. @Configuration
  13. @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
  14. protected static class DefaultFeignTargeterConfiguration {
  15. @Bean
  16. @ConditionalOnMissingBean
  17. public Targeter feignTargeter() {
  18. return new DefaultTargeter();
  19. }
  20. }

在默认情况下,feign是和hystrix整合的,feign.hystrix.HystrixFeign会有配置,所以这里默认Targeter使用的是HystrixTargeter.

3.1.3 Client

下面接着两个客户端( HttpClient, Okhttp) 的配置:

  1. @Configuration
  2. @ConditionalOnClass(ApacheHttpClient.class)
  3. @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
  4. @ConditionalOnMissingBean(CloseableHttpClient.class)
  5. @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
  6. protected static class HttpClientFeignConfiguration {
  7. ....
  8. @Bean
  9. @ConditionalOnMissingBean(Client.class)
  10. public Client feignClient(HttpClient httpClient) {
  11. return new ApacheHttpClient(httpClient);
  12. }
  13. }
  14. @Configuration
  15. @ConditionalOnClass(OkHttpClient.class)
  16. @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
  17. @ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
  18. @ConditionalOnProperty("feign.okhttp.enabled")
  19. protected static class OkHttpFeignConfiguration {
  20. ....
  21. @Bean
  22. public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
  23. ConnectionPool connectionPool,
  24. FeignHttpClientProperties httpClientProperties) {
  25. Boolean followRedirects = httpClientProperties.isFollowRedirects();
  26. Integer connectTimeout = httpClientProperties.getConnectionTimeout();
  27. Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
  28. this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
  29. .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
  30. .followRedirects(followRedirects).connectionPool(connectionPool)
  31. .build();
  32. return this.okHttpClient;
  33. }
  34. }

从装配条件就可以知道,它们都没有实现负载均衡。

小结: 至此 FeignAutoConfiguration 类就分析完了,它完成了 feignContext,Targeter, Client等的创建,并装配好了 encoder,decoder,contract,logger等对象

3.2 FeignRibbonClientAutoConfiguration

  1. @ConditionalOnClass({ ILoadBalancer.class, Feign.class })
  2. @Configuration
  3. @AutoConfigureBefore(FeignAutoConfiguration.class) //先运行 FeignAutoConfiguration
  4. @EnableConfigurationProperties({ FeignHttpClientProperties.class })
  5. // 这里导入的顺序很重要,default必须放在最后
  6. @Import({ HttpClientFeignLoadBalancedConfiguration.class,
  7. OkHttpFeignLoadBalancedConfiguration.class,
  8. DefaultFeignLoadBalancedConfiguration.class })
  9. public class FeignRibbonClientAutoConfiguration {
  10. @Bean
  11. @Primary
  12. @ConditionalOnMissingBean
  13. @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
  14. public CachingSpringLoadBalancerFactory cachingLBClientFactory(
  15. SpringClientFactory factory) {
  16. return new CachingSpringLoadBalancerFactory(factory);
  17. }
  18. }

小结: FeignRibbonClientAutoConfiguration 实现了负载均衡。 创建CachingSpringLoadBalancerFactory时注入的参数

SpringClientFactory是一个工厂,它用于创建客户端,负载均衡器及客户端配置实例等,针对每个客户端名它创建一个Spring容器。 实际是 RibbonClientFactory,功能等同于 FeignContext,用于装配 Ribbon 的基本组件.

  1. FeignRibbonClientAutoConfiguration 之上 import 了三个配置类,HttpClientFeignLoadBalancedConfigurationOkHttpFeignLoadBalancedConfigurationDefaultFeignLoadBalancedConfiguration。它们都是实现了负载均衡的类,打开一个研究一下, 会发现 它创建的feignClient 对象中就注入了上面创建的 CachingSpringLoadBalancerFactory对象。 这样就可以创建一个 LoadBalancerFeignClient, 从名字上可以看出它具有负载均衡功能。
  2. @Configuration
  3. class DefaultFeignLoadBalancedConfiguration {
  4. @Bean
  5. @ConditionalOnMissingBean
  6. public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
  7. SpringClientFactory clientFactory) {
  8. // cachingFactory 用于组装 FeignLoadBalancer
  9. return new LoadBalancerFeignClient(new Client.Default(null, null),
  10. cachingFactory, clientFactory);
  11. }
  12. }

至此,一个带负载均衡功能的 FeignClient已经创建出来了。

4. 通过 @EnableFeignClients 自动扫描,完成的装配分析

@EnableFeignClients是加在我们写的程序代码的启动类上了, 从启动类中的@EnableFeignClients开始

  1. @SpringBootApplication
  2. @EnableFeignClients("com.yc.springcloud2.service") // 默认扫描 FeignApplication.class 包下 @FeignClient 注解
  3. public class FeignApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(FeignApplication.class);
  6. }
  7. }
  8. @Import(FeignClientsRegistrar.class)
  9. public @interface EnableFeignClients {
  10. String[] value() default {}; //指定扫描包路径
  11. String[] basePackages() default {}; // 包扫描路径
  12. Class<?>[] defaultConfiguration() default {};// 默认配置
  13. Class<?>[] basePackageClasses() default {};
  14. ....
  15. }

总结: 从 @EnableFeignClients 的属性大致可以推断出,FeignClientsRegistrar 实现了spring 的ImportBeanDefinitionRegistrar接口,并实现了这个接口中最重要的回调方法registerBeanDefinitions,它会在spring 容器启动时扫描 basePackages 包下的所有 @FeignClient 注解的类,用 Feign.Builder 生成动态代理的 Bean 注入到 Spring 容器中。

4.1 FeignClientsRegistrar: 注册类

  1. class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
  2. @Override
  3. public void registerBeanDefinitions(AnnotationMetadata metadata,
  4. BeanDefinitionRegistry registry) {
  5. // 注册 @EnableFeignClients#defaultConfiguration 默认配置类
  6. registerDefaultConfiguration(metadata, registry);
  7. // 扫描所有的 @FeignClient 注解
  8. registerFeignClients(metadata, registry);
  9. }
  10. }

4.1.2 registerDefaultConfiguration 注册 @EnableFeignClients#defaultConfiguration 默认配置类

  1. private void registerDefaultConfiguration(AnnotationMetadata metadata,
  2. BeanDefinitionRegistry registry) {
  3. Map<String, Object> defaultAttrs = metadata
  4. .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
  5. if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
  6. String name;
  7. if (metadata.hasEnclosingClass()) {
  8. name = "default." + metadata.getEnclosingClassName();
  9. }
  10. else {
  11. name = "default." + metadata.getClassName();
  12. }
  13. registerClientConfiguration(registry, name,
  14. defaultAttrs.get("defaultConfiguration")); // 将 @EnableFeignClients 的默认配置注入到容器中。
  15. }
  16. }
  17. private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
  18. Object configuration) {
  19. BeanDefinitionBuilder builder = BeanDefinitionBuilder
  20. .genericBeanDefinition(FeignClientSpecification.class);
  21. builder.addConstructorArgValue(name);
  22. builder.addConstructorArgValue(configuration);
  23. registry.registerBeanDefinition(
  24. name + "." + FeignClientSpecification.class.getSimpleName(),
  25. builder.getBeanDefinition());
  26. }
  27. 还记得 FeignAutoConfiguration 自动装配 FeignContext 时的 `List<FeignClientSpecification> configurations` 吗,就是将 @EnableFeignClients @FeignClient configuration 属性注册到 Spring 的容器中

4.1.3 registerFeignClients

  1. registerFeignClients @FeignClient 标注的接口装配成 FeignClientFactoryBean 注入到容器中.FeignClientFactoryBean\#getObject 最终会调用 feign.target 生成最终的代理对象。
  2. public void registerFeignClients(AnnotationMetadata metadata,
  3. BeanDefinitionRegistry registry) {
  4. ClassPathScanningCandidateComponentProvider scanner = getScanner(); //类路径扫描器
  5. scanner.setResourceLoader(this.resourceLoader); //资源加载器
  6. // 扫描条件: @FeignClient
  7. Set<String> basePackages; //要扫描的包
  8. Map<String, Object> attrs = metadata
  9. .getAnnotationAttributes(EnableFeignClients.class.getName()); // 只要加了EnableFeignClients注解的类
  10. AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
  11. FeignClient.class); //过滤出带 @FeignClient
  12. scanner.addIncludeFilter(annotationTypeFilter);
  13. basePackages = getBasePackages(metadata);
  14. ...
  15. // 扫描 basePackage 下的 @FeignClient 注解
  16. for (String basePackage : basePackages) {
  17. Set<BeanDefinition> candidateComponents = scanner
  18. .findCandidateComponents(basePackage);
  19. for (BeanDefinition candidateComponent : candidateComponents) {
  20. if (candidateComponent instanceof AnnotatedBeanDefinition) {
  21. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
  22. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
  23. Map<String, Object> attributes = annotationMetadata
  24. .getAnnotationAttributes(
  25. FeignClient.class.getCanonicalName());
  26. String name = getClientName(attributes);
  27. // 注册 @FeignClient 的配置
  28. registerClientConfiguration(registry, name,
  29. attributes.get("configuration"));
  30. // 将该接口通过 FeignClientFactoryBean 注入到容器中
  31. registerFeignClient(registry, annotationMetadata, attributes);
  32. }
  33. }
  34. }
  35. }
  36. // 注册 FeignClientFactoryBean
  37. private void registerFeignClient(BeanDefinitionRegistry registry,
  38. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
  39. String className = annotationMetadata.getClassName();
  40. BeanDefinitionBuilder definition = BeanDefinitionBuilder
  41. .genericBeanDefinition(FeignClientFactoryBean.class); // beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是FeignClientFactoryBean类
  42. ...
  43. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  44. ...
  45. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
  46. new String[] { alias });
  47. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  48. }
  49. 至此,我们总算看到 Bean 的注册了,在`registerFeignClient()`方法中构造了一个BeanDefinitionBuilder对象,BeanDefinitionBuilder的主要作用就是构建一个AbstractBeanDefinitionAbstractBeanDefinition类最终被构建成一个BeanDefinitionHolder 然后注册到Spring中。

BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); // beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是FeignClientFactoryBean类

  1. 我们知道 **FeignClientFactoryBean Spring Bean 工厂类,那么每次在Spring Context 创建实体类的时候会调用它的`getObject()`方法。。所以在 getObject 中一定可以看到类似 feign.target 的代码。 这一步至关重要,前面我们做的只是创建好了各种组件Encoder,Decoder,Contract等等,只有在这里才利用SpringIOC,DI完成最终的装配工作并生成代理对象. **

4.2 ****FeignClientFactoryBean.getObject()解析

这里直接分析FeignClientFactoryBean.getObject()方法得到代理对象,这里包含着Feign动态代理的原理

  1. @Override
  2. public Object getObject() throws Exception {
  3. return getTarget(); //得到代理对象
  4. }
  5. <T> T getTarget() {
  6. // 1. FeignAutoConfiguration 自动装配 FeignContext, 可以类比于ribbon中的SpringClientFactory,每个服务都对应一个独立的spring容器
  7. FeignContext context = this.applicationContext.getBean(FeignContext.class);
  8. // builder中包含contract、logLevel、encoder、decoder、options等信息
  9. Feign.Builder builder = feign(context);
  10. // 2. 如果@FeignClient注解上没有指定url,说明是要用ribbon的负载均衡
  11. if (!StringUtils.hasText(this.url)) {
  12. if (!this.name.startsWith("http")) {
  13. this.url = "http://" + this.name;
  14. }
  15. else {
  16. this.url = this.name;
  17. }
  18. // 这里构建的url类似于:http://serviceA
  19. this.url += cleanPath();
  20. return (T) loadBalance(builder, context,
  21. new HardCodedTarget<>(this.type, this.name, this.url));
  22. }
  23. // 3. url存在,不用负载均衡
  24. if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
  25. this.url = "http://" + this.url;
  26. }
  27. String url = this.url + cleanPath();
  28. //获取 Client,实际上是 LoadBalancerFeignClient 对象
  29. Client client = getOptional(context, Client.class);
  30. if (client != null) {
  31. if (client instanceof LoadBalancerFeignClient) {
  32. client = ((LoadBalancerFeignClient) client).getDelegate();
  33. }
  34. builder.client(client);
  35. }
  36. // 4 FeignAutoConfiguration 自动装配 Targeter
  37. Targeter targeter = get(context, Targeter.class);
  38. // 调用 feign.target 生成动态代理
  39. return (T) targeter.target(this, builder, context,
  40. new HardCodedTarget<>(this.type, this.name, url));
  41. }

以上步骤小结(可以与前面 Feign详解3-工作原理 结合看) :

  1. 获取FeignContext

    FeignContext context = this.applicationContext.getBean(FeignContext.class);

这个FeignContext对象代表一个服务所关联的Spring容器,它有自己独立的一些组件,如Logger, Decoder,Encoder组件等. 通过追踪 feign( context )方法可以证明以上观点:

  1. protected Feign.Builder feign(FeignContext context) {
  2. FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
  3. Logger logger = loggerFactory.create(this.type);
  4. // @formatter:off
  5. Feign.Builder builder = get(context, Feign.Builder.class)
  6. // required values
  7. .logger(logger)
  8. .encoder(get(context, Encoder.class))
  9. .decoder(get(context, Decoder.class))
  10. .contract(get(context, Contract.class));
  11. // @formatter:on
  12. configureFeign(context, builder);
  13. return builder;
  14. }

另外, FeignContext对象是通过本文最上面的 FeignAutoConfiguration 自动装配进来的.

  1. 创建builder对象,包括了要装配的decoder,encoder,contract,….

    Feign.Builder builder = feign(context);

  2. 获取 Client,实际上是 LoadBalancerFeignClient 对象, 并装配到builder中

    Client client = getOptional(context, Client.class);

    1. if (client != null) {
    2. if (client instanceof LoadBalancerFeignClient) {
    3. // not load balancing because we have a url,
    4. // but ribbon is on the classpath, so unwrap
    5. client = ((LoadBalancerFeignClient) client).getDelegate();
    6. }
    7. builder.client(client);
    8. }
  3. 获取Targeter对象

    Targeter targeter = get(context, Targeter.class);

  4. 通过 target()创建代理对象。

    (T) targeter.target(this, builder, context,

    1. new HardCodedTarget<>(this.type, this.name, url));

总结: 至此,@FeignClient 标注的接口,最终通过 targeter.target 生成最终的代理对象。

然后再追踪Tageter的代码:

  1. interface Targeter {
  2. <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
  3. FeignContext context, Target.HardCodedTarget<T> target);
  4. }

这个接口的实现有两种: DefaultTargeter和HystrixTargeter。

DefaultTargeter的target()方法就是 前面 [ Feign详解3-工作原理 的 “2.2 生成代理对象” ]的部分了.

  1. class DefaultTargeter implements Targeter {
  2. @Override
  3. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
  4. FeignContext context, Target.HardCodedTarget<T> target) {
  5. return feign.target(target);
  6. }
  7. }
  8. public <T> T target(Target<T> target) {
  9. return build().newInstance(target);
  10. }
  11. v^v 终于到了这个位置了,其它的就可以沿着 [Feign详解3-工作原理][Feign_3-] "2.2 生成代理对象" 向下看了.

HystrixTargeter的target()方法也差不多,只是加入了fallback回退处理.

  1. @Override
  2. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
  3. FeignContext context, Target.HardCodedTarget<T> target) {
  4. if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
  5. return feign.target(target);
  6. }
  7. feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
  8. String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
  9. : factory.getContextId();
  10. SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
  11. if (setterFactory != null) {
  12. builder.setterFactory(setterFactory);
  13. }
  14. Class<?> fallback = factory.getFallback();
  15. if (fallback != void.class) {
  16. return targetWithFallback(name, context, target, builder, fallback);
  17. }
  18. Class<?> fallbackFactory = factory.getFallbackFactory();
  19. if (fallbackFactory != void.class) {
  20. return targetWithFallbackFactory(name, context, target, builder,
  21. fallbackFactory);
  22. }
  23. return feign.target(target);
  24. }

而且它最终还是调用了 feign.target(), 殊途同归了。 哈哈,源码分析就这么简单.

最后用一张图总结Feign动态代理生成的规则:

  1. 生成Feign.builder(),里面包含Encoder、Decoder、Logger等组件,还有application.properties中相关的feign client配置信息
  2. 生成Feign.client(),默认为LoadBalancerFeignClient
  3. 生成默认Targter对象:DefaultTargeter或HystrixTargter
  4. builder、client、targter 通过JDK动态代理生成feign动态代理对象

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70 1

发表评论

表情:
评论列表 (有 0 条评论,642人围观)

还没有评论,来说两句吧...

相关阅读