Java SpringCloud @EnableFeignClients

1、概述

Spring cloud应用中,当要使用feign客户端时,一般要做以下三件事情 :

1.1 使用注解@EnableFeignClients启用feign客户端;

示例 :

  1. @SpringBootApplication
  2. @EnableFeignClients
  3. public class TestApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(TestApplication.class, args);
  6. }
  7. }

1.2 使用注解@FeignClient 定义feign客户端 ;

示例 : 该例子定义了一个feign客户端,将远程服务http://test-service/test/echo映射为一个本地Java方法调用。

  1. @FeignClient(name = "test-service", path = "/test")
  2. public interface TestService {
  3. @RequestMapping(value = "/echo", method = RequestMethod.GET)
  4. TestModel echo(@RequestParam("parameter") String parameter);
  5. }

1.3 使用注解@Autowired使用上面所定义feign的客户端 ;

  1. @Autowired
  2. TestService testService;
  3. public void run()
  4. {
  5. // 这里的使用本地Java API的方式调用远程的Restful接口
  6. TestModel dto = testService.echo("Hello,你好!");
  7. log.info("echo : {}", dto);
  8. }

上面的三个步骤,前两个步骤可以理解为定义feign客户端,第三步是使用所定义的feign客户端。通过调试发现,上面第三步所注入的testService是一个代理对象,如下所示 :

  1. testService = {$Proxy66@5502}
  2. "HardCodedTarget(type=TestService, name=test-service, url=http://test-service/test)"
  3. h = {ReflectiveFeign$FeignInvocationHandler@6924}
  4. target = {Target$HardCodedTarget@6930}
  5. dispatch = {LinkedHashMap@6931} size = 1
  6. 0 = {LinkedHashMap$Entry@6948}
  7. "public abstract xxx.model.TestModel xxx.service.TestService.echo(java.lang.String)"

该对象会代理客户端完成远程服务方法的调用,那么,该代理对象是如何生成的 ?通过源代码分析来回答这些问题。

2、源代码解析

源代码版本 : spring-cloud-openfeign-core-2.1.0.RELEASE , Spring Cloud Greenwich.RELEASE

2.1 注解@EnableFeignClients:扫描和注册feign客户端bean定义

注解@EnableFeignClients告诉框架扫描所有使用注解@FeignClient定义的feign客户端。它又通过注解@Import导入了类FeignClientsRegistrar( feign客户端注册器),如下所示:

  1. @EnableFeignClients
  2. => @Import(FeignClientsRegistrar.class)

那么 FeignClientsRegistrar 又是做什么的呢 ?继续分析。

2.2 FeignClientsRegistrar : feign客户端注册器

FeignClientsRegistrar实现了接口 ImportBeanDefinitionRegistrar。而ImportBeanDefinitionRegistrar的设计目的,就是被某个实现类实现,配合使用@Configuration注解的使用者配置类,在配置类被处理时,用于额外注册一部分bean定义:

对于上面的例子,使用者配置类就是 TestApplication

  1. public interface ImportBeanDefinitionRegistrar {
  2. /**
  3. * Register bean definitions as necessary based on the given annotation metadata of
  4. * the importing @Configuration class.
  5. * 根据使用者配置类的注解元数据注册bean定义
  6. * @param importingClassMetadata 使用者配置类的注解元数据
  7. * @param registry 当前bean定义注册表,一般指当前Spring应用上下文对象,当前Spring容器
  8. */
  9. public void registerBeanDefinitions(
  10. AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
  11. }

2.3 registerBeanDefinitions – 注册feign客户端配置和feign客户端

方法FeignClientsRegistrar#registerBeanDefinitions实现如下:

  1. @Override
  2. public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
  3. // 注册缺省配置到容器 registry
  4. registerDefaultConfiguration(metadata, registry);
  5. // 注册所发现的各个 feign 客户端到到容器 registry
  6. registerFeignClients(metadata, registry);
  7. }

2.4 registerDefaultConfiguration– 注册feign客户端缺省配置

  1. // 注册feign客户端的缺省配置,缺省配置信息来自注解元数据的属性 defaultConfiguration
  2. private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
  3. // 获取注解@EnableFeignClients的注解属性
  4. Map<String, Object> defaultAttrs = metadata
  5. .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
  6. if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
  7. String name;
  8. // 下面是对所注册的缺省配置的的命名,格式如下 :
  9. // default.xxx.TestApplication
  10. if (metadata.hasEnclosingClass()) {
  11. // 针对注解元数据metadata对应一个内部类或者方法返回的方法本地类的情形
  12. name = "default." + metadata.getEnclosingClassName();
  13. }
  14. else {
  15. // name 举例 : default.xxx.TestApplication
  16. // 这里 xxx.TestApplication 是注解@EnableFeignClients所在配置类的长名称
  17. name = "default." + metadata.getClassName();
  18. }
  19. // 各种信息准备就绪,现在执行注册
  20. registerClientConfiguration(registry, name,
  21. defaultAttrs.get("defaultConfiguration"));
  22. }
  23. }

#registerDefaultConfiguration方法最终注册客户端缺省配置的动作交给方法#registerClientConfiguration执行。

2.5 registerClientConfiguration – 注册feign客户端配置

  1. // 将指定feign客户端配置configuration作为一个bean定义注册到容器:
  2. // bean 定义对象类型 : GenericBeanDefinition
  3. // bean class : FeignClientSpecification
  4. // bean name : default.xxx.TestApplication.FeignClientSpecification (缺省配置)
  5. // bean name : test-service.FeignClientSpecification (针对某个feign client 的配置)
  6. private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
  7. Object configuration) {
  8. BeanDefinitionBuilder builder = BeanDefinitionBuilder
  9. .genericBeanDefinition(FeignClientSpecification.class);
  10. // 设置构造函数参数
  11. builder.addConstructorArgValue(name);
  12. builder.addConstructorArgValue(configuration);
  13. // 从bean定义构建器构造bean定义并注册到容器
  14. registry.registerBeanDefinition(
  15. name + "." + FeignClientSpecification.class.getSimpleName(),
  16. builder.getBeanDefinition());
  17. }

#registerClientConfiguration方法用于注册一个feign客户端配置bean,可以用于注册针对所有feign客户端的缺省配置的注册,也可以用于针对每个feign客户端的专有配置的注册。
针对所有feign客户端的缺省配置的bean名称类似于 : default.xxx.TestApplication.FeignClientSpecification
针对某个名称为test-servicefeign客户端的配置的bean名称类似于:test-service.FeignClientSpecification

2.6 registerFeignClients – 注册各个feign客户端及其配置

  1. // 参数 metadata : 注解@EnableFeignClients所在配置类的注解元数据
  2. public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
  3. // 定义一个基于classpath的组件扫描器,它会根据指定的扫描位置和@EnableFeignClients注解属性
  4. // 找出开发人员定义的所有feign客户端,也就是那些使用了注解@FeignClient的所有接口定义
  5. ClassPathScanningCandidateComponentProvider scanner = getScanner();
  6. scanner.setResourceLoader(this.resourceLoader);
  7. Set<String> basePackages;
  8. // attrs 用于表示注解@EnableFeignClients所在配置类的注解元数据中注解@EnableFeignClients
  9. // 的部分
  10. Map<String, Object> attrs = metadata
  11. .getAnnotationAttributes(EnableFeignClients.class.getName());
  12. AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
  13. FeignClient.class);
  14. final Class<?>[] clients = attrs == null ? null
  15. : (Class<?>[]) attrs.get("clients");
  16. if (clients == null || clients.length == 0) {
  17. // @EnableFeignClients 中没有指定 clients 属性的情况
  18. scanner.addIncludeFilter(annotationTypeFilter);
  19. basePackages = getBasePackages(metadata);
  20. }
  21. else {
  22. // @EnableFeignClients 中指定了 clients 属性的情况
  23. final Set<String> clientClasses = new HashSet<>();
  24. basePackages = new HashSet<>();
  25. for (Class<?> clazz : clients) {
  26. basePackages.add(ClassUtils.getPackageName(clazz));
  27. clientClasses.add(clazz.getCanonicalName());
  28. }
  29. AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
  30. @Override
  31. protected boolean match(ClassMetadata metadata) {
  32. String cleaned = metadata.getClassName().replaceAll("\\$", ".");
  33. return clientClasses.contains(cleaned);
  34. }
  35. };
  36. scanner.addIncludeFilter(
  37. new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
  38. }
  39. // 使用 scanner 扫描每一个 basePackage, 获取其中的 feign 客户端定义,
  40. // 也就是 @FeignClient 定义的那些接口
  41. for (String basePackage : basePackages) {
  42. Set<BeanDefinition> candidateComponents = scanner
  43. .findCandidateComponents(basePackage);
  44. for (BeanDefinition candidateComponent : candidateComponents) {
  45. if (candidateComponent instanceof AnnotatedBeanDefinition) {
  46. // verify annotated class is an interface
  47. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
  48. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
  49. Assert.isTrue(annotationMetadata.isInterface(),
  50. "@FeignClient can only be specified on an interface");
  51. // 获取所定义的feign客户端接口上的注解@FeignClient属性
  52. Map<String, Object> attributes = annotationMetadata
  53. .getAnnotationAttributes(
  54. FeignClient.class.getCanonicalName());
  55. String name = getClientName(attributes);
  56. // 将所定义的feign客户端上的配置属性作为一个bean注册到容器
  57. // 此方法的逻辑我们上面已经分析过
  58. registerClientConfiguration(registry, name,
  59. attributes.get("configuration"));
  60. // 将所定义的feign客户端作为一个bean注册到容器:
  61. // bean 定义类型 : GenericBeanDefinition
  62. // bean class : FeignClientFactoryBean
  63. // autowire 模式 : 根据类型绑定
  64. // @FeignClient注解中的url,path,fallback等属性会设置为bean定义的属性
  65. registerFeignClient(registry, annotationMetadata, attributes);
  66. }
  67. }
  68. }
  69. }
  70. // 辅助工具类,从@EnableFeignClients注解属性中获取basePackages属性:
  71. // 参考以下@EnableFeignClients注解属性 :
  72. // 1. value
  73. // 2. basePackages
  74. // 3. basePackageClasses
  75. // 4. 配置类所在的包
  76. // 参数 importingClassMetadata : 使用注解@EnableFeignClients的配置类的元数据
  77. protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
  78. // 注解@EnableFeignClients的属性
  79. Map<String, Object> attributes = importingClassMetadata
  80. .getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
  81. Set<String> basePackages = new HashSet<>();
  82. for (String pkg : (String[]) attributes.get("value")) {
  83. if (StringUtils.hasText(pkg)) {
  84. basePackages.add(pkg);
  85. }
  86. }
  87. for (String pkg : (String[]) attributes.get("basePackages")) {
  88. if (StringUtils.hasText(pkg)) {
  89. basePackages.add(pkg);
  90. }
  91. }
  92. for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
  93. basePackages.add(ClassUtils.getPackageName(clazz));
  94. }
  95. if (basePackages.isEmpty()) {
  96. basePackages.add(
  97. ClassUtils.getPackageName(importingClassMetadata.getClassName()));
  98. }
  99. return basePackages;
  100. }

#registerFeignClients 最终注册feign客户端配置的动作交给#registerClientConfiguration完成,而注册feign客户端的动作交给#registerFeignClient方法完成。

2.7 registerFeignClient – 注册一个feign客户端

  1. // 将所定义的feign客户端作为一个bean注册到容器:
  2. // bean 定义类型 : GenericBeanDefinition
  3. // bean class : FeignClientFactoryBean -- 这是一个工厂bean,而不是最终bean实例的class
  4. // autowire 模式 : 根据类型绑定
  5. // @FeignClient注解中的url,path,fallback等属性会设置为bean定义的属性
  6. // 参数 registry : Spring 容器
  7. // 参数 annotationMetadata : @FeignClient所注解的接口上的注解元数据
  8. // 参数 attributes : @FeignClient 注解属性信息
  9. private void registerFeignClient(BeanDefinitionRegistry registry,
  10. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
  11. String className = annotationMetadata.getClassName();
  12. BeanDefinitionBuilder definition = BeanDefinitionBuilder
  13. .genericBeanDefinition(FeignClientFactoryBean.class);
  14. validate(attributes);
  15. definition.addPropertyValue("url", getUrl(attributes));
  16. definition.addPropertyValue("path", getPath(attributes));
  17. String name = getName(attributes);
  18. definition.addPropertyValue("name", name);
  19. definition.addPropertyValue("type", className);
  20. definition.addPropertyValue("decode404", attributes.get("decode404"));
  21. definition.addPropertyValue("fallback", attributes.get("fallback"));
  22. definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
  23. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  24. String alias = name + "FeignClient";
  25. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  26. boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
  27. beanDefinition.setPrimary(primary);
  28. String qualifier = getQualifier(attributes);
  29. if (StringUtils.hasText(qualifier)) {
  30. alias = qualifier;
  31. }
  32. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
  33. new String[] { alias });
  34. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  35. }

从上面的代码分析可知,FeignClientsRegistrar的主要作用如下 :

  1. 注册缺省feign客户端配置bean定义;
  2. 对于每个@FeignClient注解的feign客户端定义 :
    1. 注册一个针对该feign客户端的配置bean定义;
    2. 注册该feign客户端bean定义,指定生成bean实例采用工厂类FeignClientFactoryBean;

而且,上述功能实现在类方法FeignClientsRegistrar#registerBeanDefinitions中,这是接口ImportBeanDefinitionRegistrar所定义的方法。该方法会在@EnableFeignClients注解被处理时执行。具体的执行时调用栈如下所示:

  1. AbstractApplicationContext#invokeBeanFactoryPostProcessors
  2. => PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
  3. => foreach BeanDefinitionRegistryPostProcessor : #postProcessBeanDefinitionRegistry
  4. => ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
  5. => #processConfigBeanDefinitions
  6. => ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
  7. => foreach ConfigurationClass : #loadBeanDefinitionsForConfigurationClass
  8. => #loadBeanDefinitionsFromRegistrars
  9. => foreach ImportBeanDefinitionRegistrar : #registerBeanDefinitions
  10. => FeignClientsRegistrar#registerBeanDefinitions

2.8 FeignClientFactoryBean生成feign客户端代理对象

基于上面的分析,可以得知,开发人员所定义的feign客户端和相关配置会以bean定义的形式注册到bean容器中,这样当使用@Autowired注入一个feign客户端时,容器会使用工厂类FeignClientFactoryBean为其生成一个实例。下面来看其具体工作过程。

FeignClientFactoryBean#getObject生成feign客户端代理对象

  1. // 该方法由接口FactoryBean约定
  2. @Override
  3. public Object getObject() throws Exception {
  4. return getTarget();
  5. }
  6. <T> T getTarget() {
  7. // 从应用上下文中获取创建 feign 客户端的上下文对象 FeignContext
  8. // FeignContext 针对每个feign客户端定义会生成一个不同的 AnnotationConfigApplicationContext,
  9. // 这些应用上下文的parent都设置为当前应用的主应用上下文
  10. // 参考 : FeignAutoConfiguration
  11. FeignContext context = applicationContext.getBean(FeignContext.class);
  12. // 为目标feign客户端对象构建一个 builder,该builder最终生成的目标feign客户端是一个
  13. // 动态代理,使用 InvocationHandler :ReflectiveFeign$FeignInvocationHandler
  14. Feign.Builder builder = feign(context);
  15. if (!StringUtils.hasText(this.url)) {
  16. // @FeignClient 属性 url 属性没有指定的情况
  17. // 根据属性 name , path 拼装一个 url,
  18. // 这种通常是需要在多个服务节点之间进行负载均衡的情况
  19. if (!this.name.startsWith("http")) {
  20. url = "http://" + this.name;
  21. }
  22. else {
  23. url = this.name;
  24. }
  25. // 方法cleanPath()加工属性path,使其以/开头,不以/结尾
  26. url += cleanPath();
  27. // 这里形成的url格式类似 : http://test-service/test
  28. // 其中 test-service 是服务名,不是服务所在节点的IP,主机名或者域名
  29. // 函数 loadBalance 做如下动作 :
  30. // 1. 将builder和一个LoadBalancerFeignClient bean实例关联起来
  31. // 2. 使用一个HystrixTargeter将builder和一个 HardCodedTarget bean实例关联起来
  32. // 这里 HardCodedTarget 表示对应 url 为 http://test-service/test 的远程服务(可能
  33. // 包含多个服务方法)
  34. // 3. 生成最终的feign client 实例 : ReflectiveFeign$FeignInvocationHandler 的动态代理对象,
  35. // 使用 InvocationHandler :ReflectiveFeign$FeignInvocationHandler。
  36. // 每个远程服务方法会对应到一个@FeignClient注解的接口方法上(依据方法上的注解进行匹配)
  37. return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
  38. this.name, url));
  39. }
  40. // @FeignClient 属性 url 属性被指定的情况
  41. // 这种通常是明确指出了服务节点的url的情况,实际上不需要负载均衡
  42. if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
  43. this.url = "http://" + this.url;
  44. }
  45. String url = this.url + cleanPath();
  46. // 将builder和一个LoadBalancerFeignClient bean实例关联起来
  47. Client client = getOptional(context, Client.class);
  48. if (client != null) {
  49. if (client instanceof LoadBalancerFeignClient) {
  50. // not load balancing because we have a url,
  51. // but ribbon is on the classpath, so unwrap
  52. // 因为指定了明确的服务节点url,所以这里不需要负载均衡,
  53. // 所以这里尽管client是LoadBalancerFeignClient,所以
  54. // 实际上可以获取其所代理的对象作为最终的client,
  55. // 相当于去掉了LoadBalancerFeignClient这层的代理功能
  56. client = ((LoadBalancerFeignClient)client).getDelegate();
  57. }
  58. builder.client(client);
  59. }
  60. // 使用一个HystrixTargeter将builder和一个 HardCodedTarget bean实例关联起来
  61. Targeter targeter = get(context, Targeter.class);
  62. // 生成最终的feign client 实例 : ReflectiveFeign$FeignInvocationHandler 的动态代理对象,
  63. // 使用 InvocationHandler :ReflectiveFeign$FeignInvocationHandler。
  64. // 每个远程服务方法会对应到 一个@FeignClient注解的接口方法上(依据方法上的注解进行匹配)
  65. return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
  66. this.type, this.name, url));
  67. }

方法FeignClientFactoryBean#feign – 创建feign客户端构建器

  1. protected Feign.Builder feign(FeignContext context) {
  2. FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
  3. Logger logger = loggerFactory.create(this.type);
  4. // 从上下文获取一个 Feign.Builder 上,
  5. // 并从上下文获得 Encoder, Decoder, Contract 设置到该 builder 上
  6. Feign.Builder builder = get(context, Feign.Builder.class)
  7. // required values
  8. .logger(logger)
  9. .encoder(get(context, Encoder.class))
  10. .decoder(get(context, Decoder.class))
  11. .contract(get(context, Contract.class));
  12. // 对 builder 进行其他属性设置
  13. configureFeign(context, builder);
  14. return builder;
  15. }

方法FeignClientFactoryBean#loadBalance – 生成具备负载均衡能力的feign客户端

feign客户端构建器绑定负载均衡客户端,绑定目标服务端点,并生成最终的feign客户端实例。

  1. // 对builder设置负载均衡客户端,绑定到目标服务端点,构建最终的feign客户端对象
  2. protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
  3. HardCodedTarget<T> target) {
  4. // 从上下文context获取一个Client,缺省是 LoadBalancerFeignClient
  5. Client client = getOptional(context, Client.class);
  6. if (client != null) {
  7. // 将client设置到builder上
  8. builder.client(client);
  9. // 从上下文中获取一个 targeter,缺省是一个 HystrixTargeter
  10. Targeter targeter = get(context, Targeter.class);
  11. // 上面获取得到的 targeter 会根据 builder 的类型决定如何将 target
  12. // 绑定到 builder 并设置有关的其他属性和功能,然后生成最终的feign客户端对象
  13. return targeter.target(this, builder, context, target);
  14. }
  15. throw new IllegalStateException(
  16. "No Feign Client for loadBalancing defined. Did you forget to include " +
  17. "spring-cloud-starter-netflix-ribbon?");
  18. }

从上面分析可以看出,缺省情况下,所使用的feign客户端构建器类为Feign.Builder,并且Targeter是一个HystrixTargeterHystrixTargeter#target方法的参数builderFeign.Builder时,会直接调用该buildertarget方法,如下所示 :

  1. class HystrixTargeter implements Targeter {
  2. @Override
  3. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
  4. Target.HardCodedTarget<T> target) {
  5. if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
  6. return feign.target(target);
  7. }
  8. // ... 省略其他代码
  9. }
  10. }

接下来再来看Feign.Builder#target是如何工作的:

  1. // 执行构建并且创建相应的feign客户端实例
  2. public <T> T target(Target<T> target) {
  3. return build().newInstance(target);
  4. }
  5. // 构建过程,最终根据各种配置生成一个 ReflectiveFeign 对象
  6. public Feign build() {
  7. SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
  8. new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
  9. logLevel, decode404, closeAfterDecode, propagationPolicy);
  10. ParseHandlersByName handlersByName =
  11. new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
  12. errorDecoder, synchronousMethodHandlerFactory);
  13. return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
  14. }

然后再看ReflectiveFeign#newInstance方法:

  1. // 创建最终的feign客户端实例 : 一个 ReflectiveFeign$FeignInvocationHandler 的动态代理对象
  2. @Override
  3. public <T> T newInstance(Target<T> target) {
  4. Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  5. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  6. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  7. for (Method method : target.type().getMethods()) {
  8. if (method.getDeclaringClass() == Object.class) {
  9. continue;
  10. } else if (Util.isDefault(method)) {
  11. // 对于每个缺省方法,使用 DefaultMethodHandler
  12. DefaultMethodHandler handler = new DefaultMethodHandler(method);
  13. defaultMethodHandlers.add(handler);
  14. methodToHandler.put(method, handler);
  15. } else {
  16. // 对于每个对应服务功能端点的方法,缺省使用nameToHandler获取的MethodHandler,缺省是
  17. // SynchronousMethodHandler
  18. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  19. }
  20. }
  21. // 创建feign客户端实例 ReflectiveFeign$FeignInvocationHandler,
  22. // 该对象包含了上面所创建的methodToHandler,用于对应各个开发者定义的@FeignClient接口方法
  23. InvocationHandler handler = factory.create(target, methodToHandler);
  24. // 创建feign客户端实例的动态代理对象
  25. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
  26. new Class<?>[] {target.type()}, handler);
  27. // 将缺省方法处理器绑定到feign客户端实例的动态代理对象上
  28. for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  29. defaultMethodHandler.bindTo(proxy);
  30. }
  31. return proxy;
  32. }

从上面的分析不难看出,为什么最终注入的testService最终是一个ReflectiveFeign$FeignInvocationHandler动态代理实例了。

3、总结

从上面的分析可以看出,当使用注解@EnableFeignClients 时,相当于启用了feign客户端定义的扫描和注册机制,从而可以发现开发人员通过注解@FeignClient定义的feign客户端,并最终作为bean定义注册到容器中。
而通过@Autowired自动装配注解,这些feign客户端会以ReflectiveFeign$FeignInvocationHandler动态代理的形式被注入到使用方。该feign客户端包含了对每个接口方法的处理器MethodHandler,接口缺省方法对应DefaultMethodHandler,服务功能端点方法对应SynchronousMethodHandler