乐趣区

聊聊spring-cloud-openfeign的Targeter

本文主要研究一下 spring cloud openfeign 的 Targeter

Targeter

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/Targeter.java

interface Targeter {

    <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target);

}
  • Targeter 定义了 target 方法,它接收 FeignClientFactoryBean、Feign.Builder、FeignContext、Target.HardCodedTarget 类型的参数

DefaultTargeter

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/DefaultTargeter.java

class DefaultTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {return feign.target(target);
    }

}
  • DefaultTargeter 实现了 Targeter 接口,它的 target 方法直接使用的是 Feign.Builder.target 方法

Target

feign-core-10.2.3-sources.jar!/feign/Target.java

public interface Target<T> {/* The type of the interface this target applies to. ex. {@code Route53}. */
  Class<T> type();

  /* configuration key associated with this target. For example, {@code route53}. */
  String name();

  /* base HTTP URL of the target. For example, {@code https://api/v2}. */
  String url();

  public Request apply(RequestTemplate input);

  //......

}
  • Target 接口定义了 type、name、url、apply 方法

HardCodedTarget

feign-core-10.2.3-sources.jar!/feign/Target.java

  public static class HardCodedTarget<T> implements Target<T> {

    private final Class<T> type;
    private final String name;
    private final String url;

    public HardCodedTarget(Class<T> type, String url) {this(type, url, url);
    }

    public HardCodedTarget(Class<T> type, String name, String url) {this.type = checkNotNull(type, "type");
      this.name = checkNotNull(emptyToNull(name), "name");
      this.url = checkNotNull(emptyToNull(url), "url");
    }

    @Override
    public Class<T> type() {return type;}

    @Override
    public String name() {return name;}

    @Override
    public String url() {return url;}

    /* no authentication or other special activity. just insert the url. */
    @Override
    public Request apply(RequestTemplate input) {if (input.url().indexOf("http") != 0) {input.target(url());
      }
      return input.request();}

    @Override
    public boolean equals(Object obj) {if (obj instanceof HardCodedTarget) {HardCodedTarget<?> other = (HardCodedTarget) obj;
        return type.equals(other.type)
            && name.equals(other.name)
            && url.equals(other.url);
      }
      return false;
    }

    @Override
    public int hashCode() {
      int result = 17;
      result = 31 * result + type.hashCode();
      result = 31 * result + name.hashCode();
      result = 31 * result + url.hashCode();
      return result;
    }

    @Override
    public String toString() {if (name.equals(url)) {return "HardCodedTarget(type=" + type.getSimpleName() + ", url=" + url + ")";
      }
      return "HardCodedTarget(type=" + type.getSimpleName() + ", name=" + name + ", url=" + url
          + ")";
    }
  }
  • HardCodedTarget 实现了 Target 接口,其构造器接收 type、name、url 参数,其 apply 方法对于 url 是 http 开头的则设置 RequestTemplate 的 target 为 url,然后构造 request 返回

HystrixTargeter

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/HystrixTargeter.java

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

    private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
            Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
            Class<?> fallbackFactoryClass) {FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
                "fallbackFactory", feignClientName, context, fallbackFactoryClass,
                FallbackFactory.class);
        return builder.target(target, fallbackFactory);
    }

    private <T> T targetWithFallback(String feignClientName, FeignContext context,
            Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
            Class<?> fallback) {
        T fallbackInstance = getFromContext("fallback", feignClientName, context,
                fallback, target.type());
        return builder.target(target, fallbackInstance);
    }

    private <T> T getFromContext(String fallbackMechanism, String feignClientName,
            FeignContext context, Class<?> beanType, Class<T> targetType) {Object fallbackInstance = context.getInstance(feignClientName, beanType);
        if (fallbackInstance == null) {
            throw new IllegalStateException(String.format(
                    "No" + fallbackMechanism
                            + "instance of type %s found for feign client %s",
                    beanType, feignClientName));
        }

        if (!targetType.isAssignableFrom(beanType)) {
            throw new IllegalStateException(String.format("Incompatible"
                    + fallbackMechanism
                    + "instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
                    beanType, targetType, feignClientName));
        }
        return (T) fallbackInstance;
    }

    private <T> T getOptional(String feignClientName, FeignContext context,
            Class<T> beanType) {return context.getInstance(feignClientName, beanType);
    }

}
  • HystrixTargeter 实现了 Targeter 接口,其 target 方法首先判断 Feign.Builder 类型是否是 feign.hystrix.HystrixFeign.Builder,不是的话直接执行 feign.target(target) 返回
  • 对于 fallback 不为 void.class 的使用 targetWithFallback 进行构造;对于 fallbackFactory 不为 void.class 的使用 targetWithFallbackFactory 进行构造;都不是的话则执行 feign.target(target) 返回
  • targetWithFallbackFactory 方法使用 HystrixFeign.Builder 的 target 进行构造时使用的是 fallbackFactory;而 targetWithFallback 方法使用的是 fallbackInstance

FeignAutoConfiguration

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/FeignAutoConfiguration.java

@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
        FeignHttpClientProperties.class })
public class FeignAutoConfiguration {
    //......

    @Configuration
    @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
    protected static class HystrixFeignTargeterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {return new HystrixTargeter();
        }

    }

    @Configuration
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    protected static class DefaultFeignTargeterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {return new DefaultTargeter();
        }

    }

    //......
}
  • FeignAutoConfiguration 在有 feign.hystrix.HystrixFeign 时创建的是 HystrixTargeter,否则创建的是 DefaultTargeter

小结

  • Targeter 定义了 target 方法,它接收 FeignClientFactoryBean、Feign.Builder、FeignContext、Target.HardCodedTarget 类型的参数
  • DefaultTargeter 实现了 Targeter 接口,它的 target 方法直接使用的是 Feign.Builder.target 方法
  • HystrixTargeter 实现了 Targeter 接口,其 target 方法首先判断 Feign.Builder 类型是否是 feign.hystrix.HystrixFeign.Builder,不是的话直接执行 feign.target(target) 返回;否则对于 fallback 不为 void.class 的使用 targetWithFallback 进行构造;对于 fallbackFactory 不为 void.class 的使用 targetWithFallbackFactory 进行构造;都不是的话则执行 feign.target(target) 返回

doc

  • Targeter
退出移动版