SpringCloud 源码系列(14)— 服务调用Feign 之 构建@FeignClient接口动态代理

2,302 阅读12分钟

专栏系列文章:SpringCloud系列专栏

系列文章:

SpringCloud 源码系列(1)— 注册中心Eureka 之 启动初始化

SpringCloud 源码系列(2)— 注册中心Eureka 之 服务注册、续约

SpringCloud 源码系列(3)— 注册中心Eureka 之 抓取注册表

SpringCloud 源码系列(4)— 注册中心Eureka 之 服务下线、故障、自我保护机制

SpringCloud 源码系列(5)— 注册中心Eureka 之 EurekaServer集群

SpringCloud 源码系列(6)— 注册中心Eureka 之 总结篇

SpringCloud 源码系列(7)— 负载均衡Ribbon 之 RestTemplate

SpringCloud 源码系列(8)— 负载均衡Ribbon 之 核心原理

SpringCloud 源码系列(9)— 负载均衡Ribbon 之 核心组件与配置

SpringCloud 源码系列(10)— 负载均衡Ribbon 之 HTTP客户端组件

SpringCloud 源码系列(11)— 负载均衡Ribbon 之 重试与总结篇

SpringCloud 源码系列(12)— 服务调用Feign 之 基础使用篇

SpringCloud 源码系列(13)— 服务调用Feign 之 扫描@FeignClient注解接口

动态代理工厂组件 FeignClientFactoryBean

从前文中已经分析出 FeignClientFactoryBean 这个组件就是生成 FeignClient 接口动态代理的组件。

FeignClientFactoryBean 实现了 FactoryBean 接口,当一个Bean实现了 FactoryBean 接口后,Spring 会先实例化这个工厂,然后在需要的时候调用 getObject() 创建真正的Bean。

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

}

FeignClientFactoryBean 实现了 getObject() 方法,它又调用了 getTarget() 方法,getTarget() 最后就创建了 FeignClient 接口的动态代理对象。

创建动态代理对象的主要流程如下:

  • 首先获取了 Feign 上下文 FeignContext,FeignContext 跟 Ribbon 中 SpringClientFactory 是类似的,可以获取到每个服务的上下文。因为每个服务都有自己的配置、Encoder、Decoder 组件等,所以可以从 FeignContext 中获取到当前服务的组件。
  • 然后从 FeignContext 中得到了 Feign.Builder,这个 Feign.Builder 就是最终用来创建动态代理对象的构造器。
  • @FeignClient 如果没有配置 url,就会通过服务名称构造带服务名的url地址,跟 RestTemplate 类似,最终肯定就是走负载均衡的请求;如果配置了 url,就是直接调用这个地址。
  • 都会从 FeignContext 中获取一个 Client,如果配置了 url,就是获取 client 里的代理对象,并设置到 builder 中;否则就直接将 Client 设置到 builder。也就是说根据 url 判断是否使用负载均衡的 Client。
  • 最终都会调用 Targetertarget() 方法来构造动态代理对象,target 传入的参数包括当前的 FeignClientFactoryBean 对象、Feign.Builder、FeignContext,以及封装的 HardCodedTarget 对象。
// 获取 FeignClient 代理对象的入口
@Override
public Object getObject() throws Exception {
    return getTarget();
}

/**
 * 创建一个 FeignClient 接口的代理对象,T 就是 @FeignClient 注解的接口类型
 *
 * @param <T> the target type of the Feign client
 * @return a {@link Feign} client created with the specified data and the context information
 */
<T> T getTarget() {
    // Feign 上下文
    FeignContext context = applicationContext.getBean(FeignContext.class);
    // Feign 构造器
    Feign.Builder builder = feign(context);

    // 如果没有直接配置 url,就走负载均衡请求
    if (!StringUtils.hasText(url)) {
        if (!name.startsWith("http")) {
            url = "http://" + name;
        }
        else {
            url = name;
        }
        // 带服务名的地址 => http://demo-consumer
        url += cleanPath();
        // 返回的类型肯定是具备负载均衡能力的;HardCodedTarget => 硬编码的 Target
        return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
    }

    // 如果配置了 url,就直接请求 url 地址
    if (StringUtils.hasText(url) && !url.startsWith("http")) {
        url = "http://" + url;
    }
    String url = this.url + cleanPath();
    // Client => Feign 发起 HTTP 调用的核心组件
    Client client = getOptional(context, Client.class);
    if (client != null) {
        if (client instanceof LoadBalancerFeignClient) {
            // 得到的是代理对象,就是原生的 Client.Default
            client = ((LoadBalancerFeignClient) client).getDelegate();
        }
        if (client instanceof FeignBlockingLoadBalancerClient) {
            // 得到的是代理对象,就是原生的 Client.Default
            client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
        }
        builder.client(client);
    }
    Targeter targeter = get(context, Targeter.class);
    // targeter 创建动态代理对象
    return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
    // 获取 Client
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        // Targeter => HystrixTargeter
        Targeter targeter = get(context, Targeter.class);
        // targeter 创建动态代理对象
        return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
            "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

动态代理构造器 Feign.Builder

feign() 方法返回了 Feign.Builder,它也是从 FeignContext 中获取的,这个方法最重要的是设置了 Logger、Encoder、Decoder、Contract,并读取配置文件中 feign.client.* 相关的配置。FeignClientsConfiguration 中配置了这几个接口的默认实现类,我们也可以自定义这几个实现类。

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

    // 我们可以定制 Logger、Encoder、Decoder、Contract
    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));

    // 读取配置文件中 feign.client.* 的配置来配置 Feign
    configureFeign(context, builder);

    return builder;
}

Feign.Builder 的默认实现是什么呢?从 FeignClientsConfiguration 中可以知道,默认情况下就是 Feign.Builder,如果启用了 feign.hystrix.enabled,那默认实现就是 HystrixFeign.Builder

那 Feign.Builder 和 HystrixFeign.Build 有什么区别呢?对比下不难发现,主要区别就是创建动态代理的实现类 InvocationHandler 是不同的,在启用 hystrix 的情况下,会涉及到熔断、降级等,HystrixFeign.Build 也会设置 @FeignClient 配置的 fallback、fallbackFactory 降级配置类。这块等后面分析 hystrix 源码时再来看。现在只需要知道,feign 没有启用 hystrix,@FeignClient 配置的 fallback、fallbackFactory 降级回调是不生效的。

public class FeignClientsConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer() {
        // 从不重试
        return Retryer.NEVER_RETRY;
    }

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

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

        // 引入了 hystrix 并且,feign.hystrix.enabled = true
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled")
        public Feign.Builder feignHystrixBuilder() {
            // feign 启用 hystrix 后,Feign.Builder 就是 HystrixFeign.Builder
            return HystrixFeign.builder();
        }
    }
}

feign 配置

configureFeign() 方法就是配置 Feign.Builder 的,从这个方法可以验证基础篇文章中 feign 配置生效的优先级。

Feign 有三块配置,一个是可以通过 Configuration 的方式配置,然后设置到 @FeignClient 的 configuration 参数;然后是全局的 feign.client.default 默认配置,以及服务特定的配置 feign.client.<clientName>

configureFeign() 方法可以看出,默认情况下,优先级最低的是代码配置,其次是默认配置,最高优先级的是服务特定的配置

如果想使代码配置优先级高于文件中的配置,可以设置 feign.client.defalut-to-properties=false 来改变 Feign 配置生效的优先级。

protected void configureFeign(FeignContext context, Feign.Builder builder) {
    // 配置文件中 feign.client.* 客户端配置
    FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);

    FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class);
    setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());

    if (properties != null && inheritParentContext) {
        // defaultToProperties:优先使用配置文件中的配置
        if (properties.isDefaultToProperties()) {
            // 最低优先级:使用代码中的 Configuration 配置
            configureUsingConfiguration(context, builder);
            // 次优先级:使用 feign.client.default 默认配置
            configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
            // 高优先级:使用 feign.client.<clientName> 定义的配置
            configureUsingProperties(properties.getConfig().get(contextId), builder);
        }
        // 优先使用Java代码的配置
        else {
            configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
            configureUsingProperties(properties.getConfig().get(contextId), builder);
            configureUsingConfiguration(context, builder);
        }
    }
    else {
        configureUsingConfiguration(context, builder);
    }
}

网络调用组件 Client

Clientfeign-core 中的组件,它只有一个接口 execute,这个接口就是调用 Request 的 url,然后将返回接口封装到 Response中。

public interface Client {

  /**
   * Executes a request against its {@link Request#url() url} and returns a response.
   *
   * @param request safe to replay.
   * @param options options to apply to this request.
   * @return connected response, {@link Response.Body} is absent or unread.
   * @throws IOException on a network error connecting to {@link Request#url()}.
   */
  Response execute(Request request, Options options) throws IOException;
}

Client 有如下的一些实现类:

Client 的自动化配置类是 FeignRibbonClientAutoConfiguration,FeignRibbonClientAutoConfiguration 导入了 HttpClient、OkHttp 以及默认的 Feign 负载均衡配置类。

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled", matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
        OkHttpFeignLoadBalancedConfiguration.class,
        DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
}

启用 Apache HttpClient

HttpClientFeignLoadBalancedConfiguration 的配置可以看出,要启用 apache httpclient,需设置 feign.httpclient.enabled=true(默认为 true),并且需要加入了 feign-httpclient 的依赖(ApacheHttpClient)

启用 apache httpclient 后,LoadBalancerFeignClient 的代理对象就是 feign-httpclient 中的 ApacheHttpClient

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
            SpringClientFactory clientFactory, HttpClient httpClient) {
        ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
        return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
    }
}

启用 OkHttp

OkHttpFeignLoadBalancedConfiguration 的配置可以看出,要启用 okhttp,需设置 feign.okhttp.enabled=true,且需要引入 feign-okhttp 的依赖(OkHttpClient)。

启用 okhttp 后,LoadBalancerFeignClient 的代理对象就是 feign-okhttp 的 OkHttpClient

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
@Import(OkHttpFeignConfiguration.class)
class OkHttpFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
            SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
        OkHttpClient delegate = new OkHttpClient(okHttpClient);
        return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
    }
}

默认配置

没有引入 feign-httpclient 或者 feign-okhttp,就会走默认的 DefaultFeignLoadBalancedConfiguration。而默认的代理对象 Client.Default 其实就是使用 HttpURLConnection 发起 HTTP 调用。

@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
            SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory);
    }
}

可以看出,三个配置类创建的 Client 对象都是 LoadBalancerFeignClient,也就是支持负载均衡的请求。只是代理类不同,也就是最终发起 HTTP 调用的组件是不同的,默认配置下的代理类是 Client.Default,底层就是 HttpURLConnection。

这块其实跟分析 Ribbon 源码时,RestTemplate 的负载均衡是类似的。

动态代理目标器 Targeter

Targeter 接口只有一个接口方法,就是通过 target() 方法获取动态代理对象。Targeter 有 DefaultTargeter、HystrixTargeter 两个实现类,

interface Targeter {

    <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target);
}

FeignAutoConfiguration 配置类中可看到,只要引入了 HystrixFeign,Targeter 的默认实现就是 HystrixTargeter

HystrixTargeter 一看就是用来整合 feign 和 hystrix 的,使 feign 调用可以实现熔断、限流、降级。

public class FeignAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
    protected static class HystrixFeignTargeterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new HystrixTargeter();
        }
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    protected static class DefaultFeignTargeterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new DefaultTargeter();
        }
    }
}

可以看到 HystrixTargeter 和 DefaultTargeter 的区别就在于 HystrixTargeter 会向 Feign.Builder 设置降级回调处理类,这样 feign 调用触发熔断、降级时,就可以进入回调类处理。

它们本质上最终来说都是调用 Feign.Builder 的 target() 方法创建动态代理对象。

class HystrixTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
                        FeignContext context, Target.HardCodedTarget<T> target) {
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
            // 非 HystrixFeign.Builder 类型,就直接调用 target 方法
            return feign.target(target);
        }
        // Feign 启用了 hystrix 后,就会向 HystrixFeign.Builder 设置回调类或回调工厂
        feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
        String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName() : factory.getContextId();
        
        Class<?> fallback = factory.getFallback();
        // 设置回调类
        if (fallback != void.class) {
            return targetWithFallback(name, context, target, builder, fallback);
        }
        // 设置回调工厂类
        Class<?> fallbackFactory = factory.getFallbackFactory();
        if (fallbackFactory != void.class) {
            return targetWithFallbackFactory(name, context, target, builder, fallbackFactory);
        }
        // 调用 Feign.Builder 创建动态代理
        return feign.target(target);
    }
}
class DefaultTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {
        return feign.target(target);
    }
}

Feign.Builder 创建动态代理

前面已经分析出,Feign.Builder 的默认实现就是 Feign.Builder,HystrixTargeter 中调用了 Feign.Builder 的 target 方法来创建动态代理。

  • target 方法中首先调用 build() 方法构建出 Feign,然后调用 Feign 的 newInstance 创建动态代理对象。
  • build() 方法中首先读取配置的 Client、Retryer、Logger、Contract、Encoder、Decoder 等对象。
  • 然后获取了 InvocationHandlerFactory,默认就是 InvocationHandlerFactory.Default,这是 feign 提供的一个工厂类来创建代理对象 InvocationHandler
  • 接着创建了接口方法处理器工厂 SynchronousMethodHandler.Factory,它就是用来将接口方法封装成一个方法执行器 MethodHandler,默认实现类是 SynchronousMethodHandler
  • 还创建了 springmvc 注解处理器 ParseHandlersByName,可想而知,这就是用来处理接口中的 springmvc 注解的,将 REST 接口解析生成 MethodHandler。
  • 最后创建了 Feign 对象,实现类是 ReflectiveFeign,之后就是使用 ReflectiveFeign 来创建动态代理对象了。
public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

// 构建 Feign
public Feign build() {
    // Feign Http调用客户端,默认为 Client.Default
    Client client = Capability.enrich(this.client, capabilities);
    // 重试器,默认是重不重试
    Retryer retryer = Capability.enrich(this.retryer, capabilities);
    // Feign 请求拦截器,可以对 Feign 请求模板RequestTemplate做一些定制化处理
    List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
      .map(ri -> Capability.enrich(ri, capabilities))
      .collect(Collectors.toList());
    // 日志组件,默认为 Slf4jLogger
    Logger logger = Capability.enrich(this.logger, capabilities);
    // 接口协议组件,默认为 SpringMvcContract
    Contract contract = Capability.enrich(this.contract, capabilities);
    // 配置类
    Options options = Capability.enrich(this.options, capabilities);
    // 编码器
    Encoder encoder = Capability.enrich(this.encoder, capabilities);
    // 解码器
    Decoder decoder = Capability.enrich(this.decoder, capabilities);
    // 创建 InvocationHandler 的工厂类
    InvocationHandlerFactory invocationHandlerFactory =
      Capability.enrich(this.invocationHandlerFactory, capabilities);
    QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
    // 接口方法处理器工厂
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
      new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
          logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
    // 解析 springmvc 注解
    ParseHandlersByName handlersByName =
      new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
          errorDecoder, synchronousMethodHandlerFactory);
    // ReflectiveFeign
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

InvocationHandlerFactory 包含一个 create 接口方法,默认实现是 InvocationHandlerFactory.Default,返回的 InvocationHandler 类型是 ReflectiveFeign.FeignInvocationHandler

package feign;

public interface InvocationHandlerFactory {

  // 创建动态代理
  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  // 方法处理器
  interface MethodHandler {

    Object invoke(Object[] argv) throws Throwable;
  }

  static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }
}

接着看 ReflectiveFeign 的 newInstance() 方法:

  • newInstance 的参数 target 就是前面封装的 Target.HardCodedTarget,它封装了客户端的类型、url等属性。
  • 首先是使用 ParseHandlersByName 将 FeignClient 接口中的接口转换成 MethodHandler,实际类型就是 SynchronousMethodHandler,这个细节就不在看了。
  • 然后用 InvocationHandlerFactory 创建 InvocationHandler 代理对象,也就是 ReflectiveFeign.FeignInvocationHandler,调用动态代理对象的方法,最终都会进入到这个执行处理器里面。
  • 最后,终于看到创建动态代理的地方了,使用 Proxy 创建了 FeignClient 的动态代理对象,这个动态代理的类型就是 @FeignClient 注解的接口的类型。最后被注入到 IoC 容器后,就可以在代码中注入自己编写的 FeignClient 客户端组件了。

最终就是通过 Proxy 创建一个实现了 FeignClient 接口的动态代理,然后所有接口方法的调用都会被 FeignInvocationHandler 拦截处理。

public <T> T newInstance(Target<T> target) {
    // 使用 ParseHandlersByName 将 FeignClient 接口中的接口转换成 MethodHandler,springmvc 注解由 Contract 组件处理
    // MethodHandler => SynchronousMethodHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    // 转换成 Method - MethodHandler 映射
    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    // 用 SynchronousMethodHandler.Factory 创建 SynchronousMethodHandler
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 用 Proxy 创建动态代理,动态代理对象就是 SynchronousMethodHandler
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}

一张图总结 FeignClient 生成动态代理的流程

下面用一张图来总结下生成 FeignClient 动态代理的流程:

  • 首先 @EnableFeignClients 导入的注册器 FeignClientsRegistrar 会扫描 @FeignClient 注解的接口,并生成 FeingClientFactoryBeanBeanDefinition 注册到容器中。最后会调用 FeingClientFactoryBean 的 getObject 方法来获取接口的动态代理对象。
  • 进入 FeingClientFactoryBean 的 getObject 方法,首先获取了 FeignContext,它其实就是每个客户端的容器,类似于一个 Map 结构,缓存了客户端与容器间的关系,后续大部分组件都是从 FeignContext 中获取。
  • 从 FeignContext 中获取 Feign 构造器 Feign.Builder,并配置 Feign.Builder,配置来源有多个地方,优先级最高的是 application.yml 中的配置生效;也可以配置 feign.client.default-to-properties=false 设置Java代码配置为高优先级。
  • 接下来就要根据 @FeignClient 是否配置了 url 决定是否走负载均衡的请求,其实就是设置的 Client 不一样:
    • 如果配置了 url,表示一个具体的地址,就使用将 LoadBalancerFeignClient 的 delegate 作为 Client 设置给 Feign.Builder。
    • 如果没有配置 url,表示通过服务名请求,就将 LoadBalancerFeignClient 作为 Client 设置给 Feign.Builder。
  • 再从 FeignContext 中获取 Targeter,调用它的 target 方法来获取动态代理。
  • 在 target 方法中,先调用 Feign.Builder 的 build() 方法构建了 ReflectiveFeign
    • 先是获取代理对象工厂 InvocationHandlerFactory,用于创建 InvocationHandler
    • 然后用各个组件,构造了方法处理器工厂 SynchronousMethodHandler.Factory,接着创建了方法解析器 ParseHandlersByName
    • 最后基于 InvocationHandlerFactory 和 ParseHandlersByName 构造了 ReflectiveFeign
  • 最后调用 ReflectiveFeign 的 newInstance 方法反射创建接口的动态代理:
    • 先用方法解析器 ParseHandlersByName 解析接口,将接口解析成 SynchronousMethodHandler
    • 接着使用 InvocationHandlerFactory 创建了代理对象 InvocationHandler(ReflectiveFeign.FeignInvocationHandler)
    • 最终用 Proxy 创建动态代理对象,对象的类型就是接口的类型,代理对象就是 ReflectiveFeign.FeignInvocationHandler