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

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

系列文章:web

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

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

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

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

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

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

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

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

相关文章
相关标签/搜索