Talk about Targeter of spring cloud openfeign.

  feign

Order

This article mainly studies the Targeter of spring cloud openfeign.

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 defines a target method that receives parameters of type 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 implements the Targeter interface, and its Target method directly uses the Feign.Builder.target method.

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 interface defines type, name, url, apply method

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 implements the Target interface. Its constructor receives the type, name, and url parameters. Its apply method sets the Target of requestTemplate as the url if the url starts with http, and then constructs the Request to return

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 implements the Targeter interface. its target method first determines whether the Feign.Builder type is feign.hystrix.hystrixfeign.buil der, and if not, directly executes the feign.target(target) return
  • Use targetWithFallback to construct fallback that is not void.class; The fallbackFactory is not constructed for void.class using targetWithFallbackFactory; If none of them is true, execute feign.target(target) return
  • The targetWithfallbackFactoryï¼› method uses the FallbackFactory when it is constructed using the target of HystrixFeign.Builder; The targetWithFallback method uses 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 creates HystrixTargeter when Feign. HyStrix. HyStrixEign is present, otherwise it creates DefaultTargeter

Summary

  • Targeter defines a target method that receives parameters of type FeignClientFactoryBean, Feign.Builder, FeignContext, Target.HardCodedTarget
  • Defaulttargeter implements the Targeter interface, and its Target method directly uses the Feign.Builder.target method.
  • HystrixTargeter implements the Targeter interface. its target method first determines whether the Feign.Builder type is feign.hystrix.hystrixfeign.buil der, and if not, directly executes the feign.target(target) return. Otherwise, use targetWithFallback to construct fallback that is not void.class; The fallbackFactory is not constructed for void.class using targetWithFallbackFactory; If none of them is true, execute feign.target(target) return

doc