天天看點

Spring Cloud之Open Feign調用流程和源碼分析(一)

 Open Feign整個核心的調用過程大緻如下:

Spring Cloud之Open Feign調用流程和源碼分析(一)

 下面根據源碼詳細分析:

  1. @EnableFeignClients引入FeignClientsRegistrar類實作了ImportBeanDefinitionRegistrar接口用過registerBeanDefinitions方法向spring容器中注入FeignClientSpecification類(FeignClient需要的重試政策,逾時政策,日志等配置,如果某個服務沒有設定,則讀取預設的配置)
// 通過開啟feign注解引入FeignClientsRegistrar類
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
   //.....
}
           

 FeignClientsRegistrar實作了ImportBeanDefinitionRegistrar類,重寫registerBeanDefinitions方法可以自定義bean在spring中的注冊:

//自定義注冊bean到spring容器
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
//...
    //自定義實作注冊bean
  public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        this.registerDefaultConfiguration(metadata, registry);
        this.registerFeignClients(metadata, registry);
    }

//掃描包所有加了@FeignClient的接口類,并将其注冊到spring容器中。
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//...

        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);
                }
            }
        }
}


}
           

2.接着上面的registerFeignClient()方法,成feign client bean的包裝類FeignClientFactoryBean,并将@FeignClient注解的屬性注冊到bean中。注冊的FeignClientFactoryBean,是一個包裝了我們需要執行的rpc服務的請求的類、url、服務名稱以及回調方法等。

//生成feign client bean的包裝類FeignClientFactoryBean,并将@FeignClient注解的屬性注冊到bean中。
 private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
        this.validate(attributes);
        definition.addPropertyValue("url", this.getUrl(attributes));
        definition.addPropertyValue("path", this.getPath(attributes));
        String name = this.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(2);
        String alias = name + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        boolean primary = (Boolean)attributes.get("primary");
        beanDefinition.setPrimary(primary);
        String qualifier = this.getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }

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

FeignClientFactoryBean類定義: 

private Class<?> type;
private String name;
private String url;
private String path;
private boolean decode404;
private ApplicationContext applicationContext;
private Class<?> fallback;
private Class<?> fallbackFactory;
           

3.FeignClientFactoryBean實作了FactoryBean<Object>接口,在注解使用執行個體對象的時候會從spring容器中擷取,擷取方式就是調用FactoryBean的getObject()方法:

public interface FactoryBean<T> {
    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
           
//擷取代理對象
public Object getObject() throws Exception {
    FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
    Builder builder = this.feign(context);
    String url;
    //FeignClient不存在url的走負載均衡器
    if (!StringUtils.hasText(this.url)) {
        if (!this.name.startsWith("http")) {
            url = "http://" + this.name;
        } else {
            url = this.name;
        }

        url = url + this.cleanPath();
        return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, url));

//負載均衡
protected <T> T loadBalance(Builder builder, FeignContext context, HardCodedTarget<T> target) {
    Client client = (Client)this.getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        Targeter targeter = (Targeter)this.get(context, Targeter.class);
        return targeter.target(this, builder, context, target);
//擷取目标對象
public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {
    return feign.target(target);
}

public <T> T target(Target<T> target) {
    return this.build().newInstance(target);
}
           

4.生成預設的處理方法FeignInvocationHandler實作了jdk自帶的動态代理調用接口InvocationHandler,并且為目标類生成代理類;

public <T> T newInstance(Target<T> target) {
 //InvocationHandler最終執行的方法
Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
    //...
       //建立FeignInvocationHandler包裝類,該類實作了jdk動态代理預設需要實作的接口InvocationHandler,後續通過invoke()方法轉發到nameToHandler中處理
        InvocationHandler handler = this.factory.create(target, methodToHandler);
        //建立動态代理對象
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();
}

//建立FeignInvocationHandler包裝類Target(class、name、url),dispatchd請求分發
  public InvocationHandler create(Target target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
            return new FeignInvocationHandler(target, dispatch);
        }

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

    //擷取SynchronousMethodHandler類
   public Map<String, MethodHandler> apply(Target key) {
                   for(Iterator var4 = metadata.iterator(); var4.hasNext(); result.put(md.configKey(), this.factory.create(key, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder))) {

}
//handler重寫後的invoke()方法
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);
        }

 public MethodHandler create(Target<?> target, MethodMetadata md, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) {
            return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404);
        }


//Target的屬性
public interface Target<T> {
    Class<T> type();

    String name();

    String url();
}
           
Spring Cloud之Open Feign調用流程和源碼分析(一)

 5.然後走SynchronousMethodHandler的invoke()方法進行真正的rpc調用傳回結果。

Spring Cloud之Open Feign調用流程和源碼分析(一)
//SynchronousMethodHandler調用invoke()方法,開始執行請求
  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 var5) {
                retryer.continueOrPropagate(var5);
                if (this.logLevel != Level.NONE) {
                    this.logger.logRetry(this.metadata.configKey(), this.logLevel);
                }
            }
        }
    }
           

然後走到client真正開始執行請求的地方: 

Spring Cloud之Open Feign調用流程和源碼分析(一)

 6.預設的client是LoadBalancerFeignClient,采用負載均衡政策的方式執行我們請求:

Spring Cloud之Open Feign調用流程和源碼分析(一)

下一篇将進一步分析LoadBalancer負載均衡器,講述lb-ribbon涉及的元件和底層工作原理。