Feign源碼解析

在使用Feign接口的時候我們首先要添加@EnableFeignClient注解,注解@EnableFeignClient導(dǎo)入了類FeignClientsRegistrar,如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

}

類FeignClientsRegistrar實現(xiàn)了ImportBeanDefinitionRegistrar接口,所以在Spring實例化的過程中會回調(diào)registerBeanDefinitions()方法,此方法會注冊相關(guān)的BeanDefinition,基本上所有的注解都是這樣的一個套路。從源碼可以看到registerBeanDefinitions()先是從basePackages中掃描標(biāo)記了@FeignClient的接口,如下:

public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
    // 省略

    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidateComponents = scanner
                .findCandidateComponents(basePackage);
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                // verify annotated class is an interface
                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 = getClientName(attributes);
                registerClientConfiguration(registry, name,
                        attributes.get("configuration"));

                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }
}

然后針對掃描到的每一個@FeignClient接口調(diào)用了registerFeignClient()方法,此方法為Feign接口注冊了FeignClientFactoryBean類的BeanDefinition,如下:

private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
            .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = getContextId(attributes);
    definition.addPropertyValue("contextId", contextId);
    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);

    String alias = contextId + "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;
    }

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
            new String[] { alias });
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

通過查看FeignClientFactoryBean的源碼可以發(fā)現(xiàn)FeignClientFactoryBean實現(xiàn)了FactoryBean接口,F(xiàn)actoryBean接口是一個可以自定義實例化對象的一個接口,至此,我們已經(jīng)知道了在實例化Feign接口的時候會調(diào)用FeignClientFactoryBean的getObject()方法返回對象實例。所以就重點看下FeignClientFactoryBean的getObject()方法是如何返回一個Feign接口實例的。其相關(guān)源碼如下:

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> target) {
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        Targeter targeter = get(context, Targeter.class);
        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?");
}

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

/**
 * @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() {
    FeignContext context = applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);

    if (!StringUtils.hasText(this.url)) {
        if (!this.name.startsWith("http")) {
            url = "http://" + this.name;
        }
        else {
            url = this.name;
        }
        url += cleanPath();
        return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
                this.name, 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();
        }
        builder.client(client);
    }
    Targeter targeter = get(context, Targeter.class);
    return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
            this.type, this.name, url));
}

可以看到getObject()方法直接調(diào)用target()方法,getTarget()方法獲取了FeignContext、Feign.Builder、new HardCodedTarget實例,然后調(diào)用loadBalance()方法,此方法會從Spring上下文中找Client和Targeter并調(diào)用Targeter的target()方法生成代理對象,Targeter的實現(xiàn)類有兩個:DefaultTargeter和HystrixTargeter。DefaultTargeter的target()方法直接調(diào)用Feign.Builder的target()方法返回對象實例。HystrixTargeter的target()方法會根據(jù)Feign接口配置以及Feign.Builder的類型來決定調(diào)用DefaultTargeter的target()還是HystrixTargeter的target()返代理對象。在引入Hystrix相關(guān)依賴后就會使用HystrixTargeter,如下為HystrixTargeter的target()方法源碼:

@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                    Target.HardCodedTarget<T> target) {
    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
        return feign.target(target);
    }
    feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
    SetterFactory setterFactory = getOptional(factory.getName(), context,
        SetterFactory.class);
    if (setterFactory != null) {
        builder.setterFactory(setterFactory);
    }
    Class<?> fallback = factory.getFallback();
    if (fallback != void.class) {
        return targetWithFallback(factory.getName(), context, target, builder, fallback);
    }
    Class<?> fallbackFactory = factory.getFallbackFactory();
    if (fallbackFactory != void.class) {
        return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
    }
    
    return feign.target(target);
}

因為引入了Hystrix相關(guān)依賴所以這里的Feign.Builder類型為HystrixFeign.Builder,HystrixFeign.Builder的target()方法創(chuàng)建了匿名InvocationHandlerFactory對象和ReflectiveFeign對象并調(diào)用ReflectiveFeign的newInstance()方法返回代理對象,匿名InvocationHandlerFactory對象的create()方法返回了類HystrixInvocationHandler的對象實例,如下為HystrixFeign.Builder類的源碼片段:

public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
     return build(fallbackFactory).newInstance(target);
}

Feign build(final FallbackFactory<?> nullableFallbackFactory) {
  super.invocationHandlerFactory(new InvocationHandlerFactory() {
    @Override
    public InvocationHandler create(Target target,
                                    Map<Method, MethodHandler> dispatch) {
      return new HystrixInvocationHandler(target, dispatch, setterFactory,
          nullableFallbackFactory);
    }
  });
  super.contract(new HystrixDelegatingContract(contract));
  return super.build();
}

上面代碼中super.build()返回的即為ReflectiveFeign對象,如下:

public Feign build() {
  SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
      new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
          logLevel, decode404, closeAfterDecode, propagationPolicy);
  ParseHandlersByName handlersByName =
      new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
          errorDecoder, synchronousMethodHandlerFactory);
  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

ReflectiveFeign的newInstance()方法首先會分析Feign接口中的方法并生成MethodHandler對象,接著調(diào)用InvocationHandlerFactory的create()方法創(chuàng)建InvocationHandler并使用JDK的代理方法生成代理對象,如下:

public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

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)));
  }
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
    new Class<?>[] {target.type()}, handler);

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

此處的InvocationHandler即為上面提到的匿名InvocationHandlerFactory對象的create()方法返回的HystrixInvocationHandler的對象實例,HystrixInvocationHandler為熔斷器Hystrix的InvocationHandler實現(xiàn),其invoke()方法引入了Hystrix的熔斷模塊。當(dāng)沒有引入Hystrix當(dāng)相關(guān)依賴時InvocationHandlerFactory當(dāng)實現(xiàn)為默認(rèn)實現(xiàn)InvocationHandlerFactory.Default,此實現(xiàn)的create()方法返回的InvocationHandler為ReflectiveFeign.FeignInvocationHandler,源碼如下:

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  static final class Default implements InvocationHandlerFactory {

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

類ReflectiveFeign.FeignInvocationHandler的屬性dispatch保存了Method到MethodHandler
的映射,invoke()方法會根據(jù)Method找到MethodHandler并執(zhí)行其invoke()方法,源碼如下:

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

  return dispatch.get(method).invoke(args);
}

MethodHandler的具體實現(xiàn)為SynchronousMethodHandler,此類的invoke()方法創(chuàng)建RequestTemplate對象并交由Client執(zhí)行,調(diào)用Client的execute()方法發(fā)起Http請求并返回Response對象,完成一個請求的調(diào)用,如下:

@Override
public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template);
      } catch (RetryableException e) {
        try {
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
}

Object executeAndDecode(RequestTemplate template) throws Throwable {
    Request request = targetRequest(template);
    
    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }
    
    Response response;
    long start = System.nanoTime();
    try {
      response = client.execute(request, options);
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    // 略
}

Client的實現(xiàn)有兩個:Client.Default和LoadBalancerFeignClient,前者使用HttpURLConnection發(fā)起Http請求,后者調(diào)用FeignLoadBalancer的executeWithLoadBalancer方法使用Ribbon負(fù)載均衡發(fā)起Http請求。Client.Default源碼如下:

package feign;
public interface Client {
  Response execute(Request request, Options options) throws IOException;

  public static class Default implements Client {

    // 省略

    @Override
    public Response execute(Request request, Options options) throws IOException {
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
    }

    HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
      final HttpURLConnection connection =
          (HttpURLConnection) new URL(request.url()).openConnection();
      if (connection instanceof HttpsURLConnection) {
        HttpsURLConnection sslCon = (HttpsURLConnection) connection;
        if (sslContextFactory != null) {
          sslCon.setSSLSocketFactory(sslContextFactory);
        }
        if (hostnameVerifier != null) {
          sslCon.setHostnameVerifier(hostnameVerifier);
        }
      }
      // 略
      return connection;
    }

    Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
      int status = connection.getResponseCode();
      String reason = connection.getResponseMessage();

      // 略
      return Response.builder()
          .status(status)
          .reason(reason)
          .headers(headers)
          .request(request)
          .body(stream, length)
          .build();
    }
  }
}

LoadBalancerFeignClient類相關(guān)源碼:

public class LoadBalancerFeignClient implements Client {

    // 略

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);

            IClientConfig requestConfig = getClientConfig(options, clientName);
            return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
                    requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }

    private FeignLoadBalancer lbClient(String clientName) {
        return this.lbClientFactory.create(clientName);
    }
    
    // 略
}

總結(jié)

Feign接口運行過程大致分為如下幾個步驟:

  1. @EnableFeignClient注解導(dǎo)入了FeignClientsRegistrar類。
  2. 執(zhí)行FeignClientsRegistrar的registerBeanDefinitions()方法掃描basePackages中的Feign接口并為每個Feign接口注冊FeignClientFactoryBean的BeanDefinition。
  3. Spring在實例化Feign接口時調(diào)用FeignClientFactoryBean類的getObject()方法返回Feign接口的代理實例。
  4. 創(chuàng)建InvocationHandler對象并使用JDK代理生成代理類。
  5. 如果引入了Hystrix的相關(guān)依賴則引入Hystrix的熔斷模塊(HystrixInvocationHandler)。
  6. 如果引入了Ribbon的相關(guān)依賴則Client接口會引入Ribbon的負(fù)載均衡模塊(LoadBalancerFeignClient)。
  7. 發(fā)起Http調(diào)用,返回結(jié)果。

相關(guān)類圖如下:


image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容