Loading

源码分析

通过上面的使用过程,@EnableFeignClients和@FeignClient两个注解就实现了Feign的功能,那我们从
@EnableFeignClients注解开始分析Feign的源码

(1)EnableFeignClients注解

@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.TYPE}) 
@Documented 
@Import({FeignClientsRegistrar.class}) 
public @interface EnableFeignClients {
 //
 }
通过 @EnableFeignClients 引入了FeignClientsRegistrar客户端注册类

(2)FeignClientsRegistrar注册类

                  class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
                  ResourceLoaderAware, EnvironmentAware {
                          public void registerBeanDefinitions(AnnotationMetadata metadata,
                  BeanDefinitionRegistry registry) {
                          this.registerDefaultConfiguration(metadata, registry);
                          this.registerFeignClients(metadata, registry);
                      }
                  }


通过其类结构可知,由于实现了ImportBeanDe?nitionRegistrar接口,那么在registerBeanDe?nitions()中就会解析和注册BeanDe?nition,主要注册的对象类型有两种:

  注册缺省配置的配置信息
  注册那些添加了@FeignClient的类或接口 : 这也是我们讨论的重点

public void registerFeignClients(AnnotationMetadata metadata,
  BeanDefinitionRegistry registry) {
          ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
          scanner.setResourceLoader(this.resourceLoader);
          Map<String, Object> attrs =
  metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
          AnnotationTypeFilter annotationTypeFilter = new
  AnnotationTypeFilter(FeignClient.class);
          Class<?>[] clients = attrs == null ? null : (Class[])
  ((Class[])attrs.get("clients"));
          Object basePackages;
          if (clients != null && clients.length != 0) {
              final Set<String> clientClasses = new HashSet();
              basePackages = new HashSet();
              Class[] var9 = clients;
              int var10 = clients.length;


              for(int var11 = 0; var11 < var10; ++var11) {
                  Class<?> clazz = var9[var11];
                  ((Set)basePackages).add(ClassUtils.getPackageName(clazz));
                  clientClasses.add(clazz.getCanonicalName());
              }


              AbstractClassTestingTypeFilter filter = new
  AbstractClassTestingTypeFilter() {
                  protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$",
  ".");
                      return clientClasses.contains(cleaned);
                  }
              };
              scanner.addIncludeFilter(new
  FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter,
  annotationTypeFilter)));
          } else {
              scanner.addIncludeFilter(annotationTypeFilter);
              basePackages = this.getBasePackages(metadata);
          }


          Iterator var17 = ((Set)basePackages).iterator();

          while(var17.hasNext()) {
              String basePackage = (String)var17.next();
              Set<BeanDefinition> candidateComponents =
  scanner.findCandidateComponents(basePackage);
              Iterator var21 = candidateComponents.iterator();

              while(var21.hasNext()) {
                  BeanDefinition candidateComponent =
  (BeanDefinition)var21.next();
                  if (candidateComponent instanceof AnnotatedBeanDefinition) {
                      AnnotatedBeanDefinition beanDefinition =
  (AnnotatedBeanDefinition)candidateComponent;
                      AnnotationMetadata annotationMetadata =
  beanDefinition.getMetadata();
                      Assert.isTrue(annotationMetadata.isInterface(),
  "@FeignClient can only be specified on an interface");
                      Map<String, Object> attributes =
  annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName())
  ;
                      String name = this.getClientName(attributes);
                      this.registerClientConfiguration(registry, name,
  attributes.get("configuration"));
                      this.registerFeignClient(registry, annotationMetadata,
  attributes);
                  }
              }
          }

      }

该方法主要是扫描类路径,对所有的FeignClient生成对应的 BeanDefinitio 。同时又调用了registerClientConfiguration 注册配置的方法,这里是第二处调用。这里主要是将扫描的目录下,每个项目的配置类加载的容器当中。调用 registerFeignClient 注册对象

(3) 注册FeignClient对象

private void registerFeignClient(BeanDefinitionRegistry registry,
                                   AnnotationMetadata annotationMetadata,
  Map<String, Object> attributes) {
      // 1.获取类名称,也就是本例中的FeignService接口
      String className = annotationMetadata.getClassName();
// 2.BeanDefinitionBuilder的主要作用就是构建一个AbstractBeanDefinition
      // AbstractBeanDefinition类最终被构建成一个BeanDefinitionHolder
      // 然后注册到Spring中
      // 注意:beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回
  的是
      // FeignClientFactoryBean类
      BeanDefinitionBuilder definition = BeanDefinitionBuilder
          .genericBeanDefinition(FeignClientFactoryBean.class);
      validate(attributes);

      // 3.添加FeignClientFactoryBean的属性,
      // 这些属性也都是我们在@FeignClient中定义的属性
      definition.addPropertyValue("url", getUrl(attributes));
      definition.addPropertyValue("path", getPath(attributes));
      String name = getName(attributes);
      definition.addPropertyValue("name", name);
      definition.addPropertyValue("type", className);
      definition.addPropertyValue("decode404", attributes.get("decode404"));
      definition.addPropertyValue("fallback", attributes.get("fallback"));
      definition.addPropertyValue("fallbackFactory",
  attributes.get("fallbackFactory"));
      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

      // 4.设置别名 name就是我们在@FeignClient中定义的name属性
      String alias = name + "FeignClient";
      AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

      boolean primary = (Boolean)attributes.get("primary"); // has a default,
  won't be null

      beanDefinition.setPrimary(primary);

      String qualifier = getQualifier(attributes);
      if (StringUtils.hasText(qualifier)) {
          alias = qualifier;
      }

      // 5.定义BeanDefinitionHolder,
      // 在本例中 名称为FeignService,类为FeignClientFactoryBean
      BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition,
  className,
                                                             new String[] { alias
  });
      BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  }

通过分析可知:我们最终是向Spring中注册了一个bean,bean的名称就是类或接口的名称(也就是本例中的FeignService),bean的实现类是FeignClientFactoryBean,其属性设置就是我们在@FeignClient中定义的属性。那么下面我们在Controller中对FeignService的的引入,实际就是引入了FeignClientFactoryBean 类

(4) FeignClientFactoryBean类

我们对@EnableFeignClients注解的源码进行了分析,了解到其主要作用就是把带有@FeignClient注解的类或接口用FeignClientFactoryBean类注册到Spring中。

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
  ApplicationContextAware {
      public Object getObject() throws Exception {
          return this.getTarget();
      }
      //
  }

通过 FeignClientFactoryBean 类结构可以发现其实现了FactoryBean类,那么当从ApplicationContext中获取该bean的时候,实际调用的是其getObject()方法。返回调用getTarget()方法

<T> T getTarget() {
          FeignContext context =
  (FeignContext)this.applicationContext.getBean(FeignContext.class);
          Builder builder = this.feign(context);
          if (!StringUtils.hasText(this.url)) {
              if (!this.name.startsWith("http")) {
                  this.url = "http://" + this.name;
              } else {
                  this.url = this.name;
              }

              this.url = this.url + this.cleanPath();
              return this.loadBalance(builder, context, new
  HardCodedTarget(this.type, this.name, this.url));
          } else {
              if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
                  this.url = "http://" + this.url;
              }


              String url = this.url + this.cleanPath();
              Client client = (Client)this.getOptional(context, Client.class);
              if (client != null) {
                  if (client instanceof LoadBalancerFeignClient) {
                      client = ((LoadBalancerFeignClient)client).getDelegate();
                  }


                  builder.client(client);
              }


              Targeter targeter = (Targeter)this.get(context, Targeter.class);
              return targeter.target(this, builder, context, new
  HardCodedTarget(this.type, this.name, url));
          }
      }

FeignClientFactoryBean实现了FactoryBean的getObject、getObjectType、isSingleton方法;
实现了InitializingBean的afterPropertiesSet方法;实现了ApplicationContextAware的setApplicationContext方法


getObject调用的是getTarget方法,它从applicationContext取出FeignContext,然后构造Feign.Builder并设置了logger、encoder、decoder、contract,之后通过con?gureFeign根据FeignClientProperties来进一步配置Feign.Builder的retryer、errorDecoder、request.Options、requestInterceptors、queryMapEncoder、decode404

 

初步配置完Feign.Builder之后再判断是否需要loadBalance,如果需要则通过loadBalance方法来设置,不需要则在Client是LoadBalancerFeignClient的时候进行unwrap

(5) 发送请求

由上可知,FeignClientFactoryBean.getObject()具体返回的是一个代理类,具体为FeignInvocationHandler

      static class FeignInvocationHandler implements InvocationHandler {
          private final Target target;
          private final Map<Method, MethodHandler> dispatch;

          FeignInvocationHandler(Target target, Map<Method, MethodHandler>
  dispatch) {
              this.target = (Target)Util.checkNotNull(target, "target", new
  Object[0]);
              this.dispatch = (Map)Util.checkNotNull(dispatch, "dispatch for %s",
  new Object[]{target});
          }


          public Object invoke(Object proxy, Method method, Object[] args) throws
  Throwable {
              if (!"equals".equals(method.getName())) {
                  if ("hashCode".equals(method.getName())) {
                      return this.hashCode();
                  } else {
                      return "toString".equals(method.getName()) ? this.toString()
  : ((MethodHandler)this.dispatch.get(method)).invoke(args);
                  }
              } else {
                  try {
                      Object otherHandler = args.length > 0 && args[0] != null ?
  Proxy.getInvocationHandler(args[0]) : null;
                      return this.equals(otherHandler);
                  } catch (IllegalArgumentException var5) {
                      return false;
                  }
              }
          }


          public boolean equals(Object obj) {
              if (obj instanceof ReflectiveFeign.FeignInvocationHandler) {
                  ReflectiveFeign.FeignInvocationHandler other =
  (ReflectiveFeign.FeignInvocationHandler)obj;
                  return this.target.equals(other.target);
              } else {
                  return false;
              }
          }

          public int hashCode() {
              return this.target.hashCode();
          }

          public String toString() {
              return this.target.toString();
          }
      }

FeignInvocationHandler实现了InvocationHandler,是动态代理的代理类。
当执行非Object方法时进入到this.dispatch.get(method)).invoke(args)
dispatch是一个map集合,根据方法名称获取MethodHandler。具体实现类为SynchronousMethodHandler

  final class SynchronousMethodHandler implements MethodHandler {
      public Object invoke(Object[] argv) throws Throwable {
          RequestTemplate template = this.buildTemplateFromArgs.create(argv);
          Retryer retryer = this.retryer.clone();

          while(true) {
              try {
                  return this.executeAndDecode(template);
              } catch (RetryableException var8) {
                  //
              }
          }
      }
      Object executeAndDecode(RequestTemplate template) throws Throwable {
          Request request = this.targetRequest(template);
          if (this.logLevel != Level.NONE) {
              this.logger.logRequest(this.metadata.configKey(), this.logLevel,
  request);
          }


          long start = System.nanoTime();

          Response response;
          try {
              response = this.client.execute(request, this.options);
          } catch (IOException var15) {
              //
          }
      }
  }

SynchronousMethodHandler内部创建了一个RequestTemplate对象,是Feign中的请求模板对象。内部封装了一次请求的所有元数据。
retryer中定义了用户的重试策略。
调用executeAndDecode方法通过client完成请求处理,client的实现类是LoadBalancerFeignClient

posted @ 2021-07-27 16:05  1640808365  阅读(95)  评论(0编辑  收藏  举报