SpringCloud原理 OpenFeign之FeignClient动态代理生成原理四

pivoteic
发布于 2022-6-13 17:50
浏览
0收藏

 

(3)FeignClientsConfiguration源码剖析

 

说完NamedContextFactory,接下来我们说一下FeignClientsConfiguration的作用。

这是一个默认的配置类,里面配置了很多bean,这些bean都是生成Feign客户端动态代理的需要的,我说几个重要的。

@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
    return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}

 

这个的主要作用是用来解析@FeignClient接口中每个方法使用的springmvc的注解的,这也就是为什么FeignClient可以识别springmvc注解的原因。

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
    return Feign.builder().retryer(retryer);
}

 

用来构建动态代理的类,通过这个类的target方法,就能生成Feign动态代理

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
  protected static class HystrixFeignConfiguration {

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "feign.hystrix.enabled")
    public Feign.Builder feignHystrixBuilder() {
      return HystrixFeign.builder();
    }

}

 

这个是FeignClientsConfiguration的内部类,是用来整合hystrix的,@ConditionalOnProperty(name = "feign.hystrix.enabled"),当在配置文件配置了feign.hystrix.enabled=true的时候,就开启了hystrix整合了Feign,然后调用Feign的接口就有了限流、降级的功能。其实hystrix整合Feign很简单,就是在构造动态代理的时候加了点东西而已。其实不光是hystrix,spring cloud alibaba中的sentinel在整合Feign的适合也是按照这个套路来的。

 

(4)构建动态代理的过程源码剖析

 

说完了前置的内容,接下来我们就来看一看动态代理是如何生成的。从上面我们已经知道了,@EnableFeignClinets会扫描出每个加了@FeignClient注解的接口,然后生成对应的BeanDefinition,最后重新生成一个bean class为FeignClientFactoryBean的BeanDefinition,注册到spring容器。

 

接下来就会根据BeanDefinition来生成feign客户端的代理对象了。上面我提到,是通过FeignClientFactoryBean的getObject方法来获取到代理对象,接下来,我们就来着重分析一下getObject方法的实现。

@Override
public Object getObject() throws Exception {
    return getTarget();
}

 

getObject是调用getTarget()来获取代理对象的。

 

getTarget方法

<T> T getTarget() {
    FeignContext context = this.applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);

    if (!StringUtils.hasText(this.url)) {
      if (!this.name.startsWith("http")) {
        this.url = "http://" + this.name;
      }
      else {
        this.url = this.name;
      }
      this.url += cleanPath();
      return (T) loadBalance(builder, context,
          new HardCodedTarget<>(this.type, this.name, this.url));
    }
    if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
      this.url = "http://" + this.url;
    }
    String url = this.url + cleanPath();
    Client client = getOptional(context, Client.class);
    if (client != null) {
      if (client instanceof LoadBalancerFeignClient) {
        // not load balancing because we have a url,
        // but ribbon is on the classpath, so unwrap
        client = ((LoadBalancerFeignClient) client).getDelegate();
      }
      if (client instanceof FeignBlockingLoadBalancerClient) {
        // not load balancing because we have a url,
        // but Spring Cloud LoadBalancer is on the classpath, so unwrap
        client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
      }
      builder.client(client);
    }
    Targeter targeter = get(context, Targeter.class);
    return (T) targeter.target(this, builder, context,
        new HardCodedTarget<>(this.type, this.name, url));
  }

 

先从ioc容器中获取到FeignContext,FeignContext里面封装了每个Feign的配置,起到配置隔离的作用。

 

然后获取到一个Feign.Builder,默认是在FeignClientsConfiguration中配置的。然后调用feign方法。

protected Feign.Builder feign(FeignContext context) {
    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(this.type);

    // @formatter:off
    Feign.Builder builder = get(context, Feign.Builder.class)
        // required values
        .logger(logger)
        .encoder(get(context, Encoder.class))
        .decoder(get(context, Decoder.class))
        .contract(get(context, Contract.class));
    // @formatter:on

    //这个是从配置文件中读取feign的配置
    configureFeign(context, builder);

    return builder;
  }

 

这个方法很简单,就是从每个FeignClient对应的ioc容器中获取到对应的组件,填充到 Feign.Builder中,默认都是FeignClientsConfiguration配置的。configureFeign这个方法不用去care它,它是默认从配置文件读取feign的配置,然后对Feign.Builder进行配置,有可能会覆盖从每个FeignClient对应的ioc容器中获取到对应的组件,所以配置文件的优先级是最高的,但是一般没人这么玩,所以就不用care。

 

上面获取到的各种组件都是默认的,如果你有需要替换什么组件,都可以实现,然后通过@FeignClient的configuration配置,就可以替换这些组件。

 

文章转自公众号:三友的java日记

已于2022-6-13 17:50:51修改
收藏
回复
举报
回复
    相关推荐