@EnableFeignClients

  1. /**
  2. * Scans for interfaces that declare they are feign clients (via
  3. * {@link org.springframework.cloud.openfeign.FeignClient} <code>@FeignClient</code>).
  4. * Configures component scanning directives for use with
  5. * {@link org.springframework.context.annotation.Configuration}
  6. * <code>@Configuration</code> classes.
  7. * 扫描所有@FeignClient标注的接口
  8. * 扫描所有@Configuration标注的接口
  9. *
  10. * @author Spencer Gibb
  11. * @author Dave Syer
  12. * @since 1.0
  13. */
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Target(ElementType.TYPE)
  16. @Documented
  17. //引入了FeignClientsRegistrar
  18. @Import(FeignClientsRegistrar.class)
  19. public @interface EnableFeignClients {
  20. /**
  21. * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
  22. * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
  23. * {@code @ComponentScan(basePackages="org.my.pkg")}.
  24. * @return the array of 'basePackages'.
  25. * 扫描包路径
  26. */
  27. String[] value() default {};
  28. /**
  29. * Base packages to scan for annotated components.
  30. * <p>
  31. * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
  32. * <p>
  33. * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
  34. * package names.
  35. * @return the array of 'basePackages'.
  36. * 扫描包路径
  37. */
  38. String[] basePackages() default {};
  39. /**
  40. * Type-safe alternative to {@link #basePackages()} for specifying the packages to
  41. * scan for annotated components. The package of each class specified will be scanned.
  42. * <p>
  43. * Consider creating a special no-op marker class or interface in each package that
  44. * serves no purpose other than being referenced by this attribute.
  45. * @return the array of 'basePackageClasses'.
  46. */
  47. Class<?>[] basePackageClasses() default {};
  48. /**
  49. * A custom <code>@Configuration</code> for all feign clients. Can contain override
  50. * <code>@Bean</code> definition for the pieces that make up the client, for instance
  51. * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
  52. *
  53. * @see FeignClientsConfiguration for the defaults
  54. * @return list of default configurations
  55. *
  56. * 所有 FeignClient接口的默认配置
  57. */
  58. Class<?>[] defaultConfiguration() default {};
  59. /**
  60. * List of classes annotated with @FeignClient. If not empty, disables classpath
  61. * scanning.
  62. * @return list of FeignClient classes
  63. *
  64. * 若此项不为空,则不会去扫描所有的FeignClient
  65. */
  66. Class<?>[] clients() default {};
  67. }

FeignClientsRegistrar

  1. @Override
  2. public void registerBeanDefinitions(AnnotationMetadata metadata,
  3. BeanDefinitionRegistry registry) {
  4. registerDefaultConfiguration(metadata, registry);
  5. registerFeignClients(metadata, registry);
  6. }

registerDefaultConfiguration方法

这个方法注册注解中指定的默认配置

registerFeignClients方法

  1. 若在@EnableFeignClients中指定了clients属性,只会注册指定的
  2. @FeignCient注解只允许标注在接口上
  3. 每个@FeignCient接口都会在这个方法里做如下动作:

    1. 注册client的配置类为Bean
    2. 将接口注册成为FeignClient

      registerFeignClient

      1. private void registerFeignClient(BeanDefinitionRegistry registry,
      2. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
      3. String className = annotationMetadata.getClassName();
      4. BeanDefinitionBuilder definition = BeanDefinitionBuilder
      5. .genericBeanDefinition(FeignClientFactoryBean.class);
      6. validate(attributes);
      7. definition.addPropertyValue("url", getUrl(attributes));
      8. definition.addPropertyValue("path", getPath(attributes));
      9. String name = getName(attributes);
      10. definition.addPropertyValue("name", name);
      11. String contextId = getContextId(attributes);
      12. definition.addPropertyValue("contextId", contextId);
      13. definition.addPropertyValue("type", className);
      14. definition.addPropertyValue("decode404", attributes.get("decode404"));
      15. definition.addPropertyValue("fallback", attributes.get("fallback"));
      16. definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
      17. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      18. String alias = contextId + "FeignClient";
      19. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
      20. boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
      21. // null
      22. beanDefinition.setPrimary(primary);
      23. String qualifier = getQualifier(attributes);
      24. if (StringUtils.hasText(qualifier)) {
      25. alias = qualifier;
      26. }
      27. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
      28. new String[] { alias });
      29. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
      30. }

      为每个FeignClient接口注册一个FeignClientFactoryBeanBeanDefinition

  4. beanName默认为className

  5. alias为contextId属性+FeginClient

    注册FeignClientBean

    在分析@EnableFeginClients方法后,发现其为每个Feign接口提供了一个FeignClientFactoryBean,让我们来看看这个FactoryBean ```java class FeignClientFactoryBean

    1. implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

    /*

    • WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some
    • lifecycle race condition. */

      private Class<?> type;

      private String name;

      private String url;

      private String contextId;

      private String path;

      private boolean decode404;

      private ApplicationContext applicationContext;

      private Class<?> fallback = void.class;

      private Class<?> fallbackFactory = void.class;

      @Override public void afterPropertiesSet() throws Exception { Assert.hasText(this.contextId, “Context id must be set”); Assert.hasText(this.name, “Name must be set”); }

  1. //通过context 构建FeignBuilder
  2. protected Feign.Builder feign(FeignContext context) {
  3. FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
  4. Logger logger = loggerFactory.create(this.type);
  5. // @formatter:off
  6. Feign.Builder builder =
  7. //获取Feign.Builder的bean
  8. get(context, Feign.Builder.class)
  9. // required values
  10. .logger(logger)
  11. // 获取Encoder
  12. .encoder(get(context, Encoder.class))
  13. .decoder(get(context, Decoder.class))
  14. .contract(get(context, Contract.class));
  15. // @formatter:on
  16. configureFeign(context, builder);
  17. return builder;
  18. }
  19. protected void configureFeign(FeignContext context, Feign.Builder builder) {
  20. FeignClientProperties properties = this.applicationContext
  21. .getBean(FeignClientProperties.class);
  22. if (properties != null) {
  23. is_default_to_properties,则先Configuration、后propeties
  24. if (properties.isDefaultToProperties()) {
  25. //自定义的Configuration
  26. configureUsingConfiguration(context, builder);
  27. //默认
  28. configureUsingProperties(
  29. properties.getConfig().get(properties.getDefaultConfig()),
  30. builder);
  31. //自己的
  32. configureUsingProperties(properties.getConfig().get(this.contextId),
  33. builder);
  34. }
  35. else {
  36. //默认的
  37. configureUsingProperties(
  38. properties.getConfig().get(properties.getDefaultConfig()),
  39. builder);
  40. //自己的
  41. configureUsingProperties(properties.getConfig().get(this.contextId),
  42. builder);
  43. //configuration
  44. configureUsingConfiguration(context, builder);
  45. }
  46. }
  47. else {
  48. //Configuration
  49. configureUsingConfiguration(context, builder);
  50. }
  51. }
  52. private <T> T getOrInstantiate(Class<T> tClass) {
  53. try {
  54. return this.applicationContext.getBean(tClass);
  55. }
  56. catch (NoSuchBeanDefinitionException e) {
  57. return BeanUtils.instantiateClass(tClass);
  58. }
  59. }
  60. protected <T> T get(FeignContext context, Class<T> type) {
  61. T instance = context.getInstance(this.contextId, type);
  62. if (instance == null) {
  63. throw new IllegalStateException(
  64. "No bean found of type " + type + " for " + this.contextId);
  65. }
  66. return instance;
  67. }
  68. protected <T> T getOptional(FeignContext context, Class<T> type) {
  69. return context.getInstance(this.contextId, type);
  70. }
  71. protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
  72. HardCodedTarget<T> target) {
  73. Client client = getOptional(context, Client.class);
  74. if (client != null) {
  75. builder.client(client);
  76. Targeter targeter = get(context, Targeter.class);
  77. return targeter.target(this, builder, context, target);
  78. }
  79. throw new IllegalStateException(
  80. "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
  81. }
  82. @Override
  83. public Object getObject() throws Exception {
  84. //调用getTarget方法
  85. return getTarget();
  86. }
  87. /**
  88. * @param <T> the target type of the Feign client
  89. * T是Feign接口的类
  90. * @return a {@link Feign} client created with the specified data and the context
  91. * information
  92. */
  93. <T> T getTarget() {
  94. //获取FeignContext
  95. FeignContext context = this.applicationContext.getBean(FeignContext.class);
  96. //通过context构建 Feign
  97. Feign.Builder builder = feign(context);
  98. //若不包含 url属性,则自动生成
  99. if (!StringUtils.hasText(this.url)) {
  100. if (!this.name.startsWith("http")) {
  101. this.url = "http://" + this.name;
  102. }
  103. else {
  104. this.url = this.name;
  105. }
  106. this.url += cleanPath();
  107. //loadBalance(); 可能是个普通DefaultTargeter、或hystrix
  108. return (T) loadBalance(builder, context,
  109. new HardCodedTarget<>(this.type, this.name, this.url));
  110. }
  111. //若有url但是 不是 http开头则加入
  112. if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
  113. this.url = "http://" + this.url;
  114. }
  115. String url = this.url + cleanPath();
  116. Client client = getOptional(context, Client.class);
  117. if (client != null) {
  118. if (client instanceof LoadBalancerFeignClient) {
  119. // not load balancing because we have a url,
  120. // but ribbon is on the classpath, so unwrap
  121. //当ribbon
  122. client = ((LoadBalancerFeignClient) client).getDelegate();
  123. }
  124. if (client instanceof FeignBlockingLoadBalancerClient) {
  125. // not load balancing because we have a url,
  126. // but Spring Cloud LoadBalancer is on the classpath, so unwrap
  127. // 当 Spring Cloud LoadBalancer 在的
  128. client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
  129. }
  130. builder.client(client);
  131. }
  132. Targeter targeter = get(context, Targeter.class);
  133. return (T) targeter.target(this, builder, context,
  134. new HardCodedTarget<>(this.type, this.name, url));
  135. }
  136. private String cleanPath() {
  137. String path = this.path.trim();
  138. if (StringUtils.hasLength(path)) {
  139. if (!path.startsWith("/")) {
  140. path = "/" + path;
  141. }
  142. if (path.endsWith("/")) {
  143. path = path.substring(0, path.length() - 1);
  144. }
  145. }
  146. return path;
  147. }

}

  1. ```java
  2. @ConfigurationProperties("feign.client")
  3. public class FeignClientProperties {
  4. private boolean defaultToProperties = true;
  5. private String defaultConfig = "default";
  6. /**
  7. * client1 --> 配置
  8. * client2 --> 配置
  9. */
  10. private Map<String, FeignClientConfiguration> config = new HashMap<>();
  11. public static class FeignClientConfiguration {
  12. private Logger.Level loggerLevel;
  13. private Integer connectTimeout;
  14. private Integer readTimeout;
  15. private Class<Retryer> retryer;
  16. private Class<ErrorDecoder> errorDecoder;
  17. private List<Class<RequestInterceptor>> requestInterceptors;
  18. private Boolean decode404;
  19. private Class<Decoder> decoder;
  20. private Class<Encoder> encoder;
  21. private Class<Contract> contract;
  22. private ExceptionPropagationPolicy exceptionPropagationPolicy;
  23. }
  24. }
  1. protected void configureUsingProperties(
  2. FeignClientProperties.FeignClientConfiguration config,
  3. Feign.Builder builder) {
  4. if (config == null) {
  5. return;
  6. }
  7. if (config.getLoggerLevel() != null) {
  8. builder.logLevel(config.getLoggerLevel());
  9. }
  10. if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
  11. builder.options(new Request.Options(config.getConnectTimeout(),
  12. config.getReadTimeout()));
  13. }
  14. if (config.getRetryer() != null) {
  15. Retryer retryer = getOrInstantiate(config.getRetryer());
  16. builder.retryer(retryer);
  17. }
  18. if (config.getErrorDecoder() != null) {
  19. ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
  20. builder.errorDecoder(errorDecoder);
  21. }
  22. if (config.getRequestInterceptors() != null
  23. && !config.getRequestInterceptors().isEmpty()) {
  24. // this will add request interceptor to builder, not replace existing
  25. for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
  26. RequestInterceptor interceptor = getOrInstantiate(bean);
  27. builder.requestInterceptor(interceptor);
  28. }
  29. }
  30. if (config.getDecode404() != null) {
  31. if (config.getDecode404()) {
  32. builder.decode404();
  33. }
  34. }
  35. if (Objects.nonNull(config.getEncoder())) {
  36. builder.encoder(getOrInstantiate(config.getEncoder()));
  37. }
  38. if (Objects.nonNull(config.getDecoder())) {
  39. builder.decoder(getOrInstantiate(config.getDecoder()));
  40. }
  41. if (Objects.nonNull(config.getContract())) {
  42. builder.contract(getOrInstantiate(config.getContract()));
  43. }
  44. if (Objects.nonNull(config.getExceptionPropagationPolicy())) {
  45. builder.exceptionPropagationPolicy(config.getExceptionPropagationPolicy());
  46. }
  47. }
  1. protected void configureUsingConfiguration(FeignContext context,
  2. Feign.Builder builder) {
  3. Logger.Level level = getOptional(context, Logger.Level.class);
  4. if (level != null) {
  5. builder.logLevel(level);
  6. }
  7. Retryer retryer = getOptional(context, Retryer.class);
  8. if (retryer != null) {
  9. builder.retryer(retryer);
  10. }
  11. ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
  12. if (errorDecoder != null) {
  13. builder.errorDecoder(errorDecoder);
  14. }
  15. Request.Options options = getOptional(context, Request.Options.class);
  16. if (options != null) {
  17. builder.options(options);
  18. }
  19. Map<String, RequestInterceptor> requestInterceptors = context
  20. .getInstances(this.contextId, RequestInterceptor.class);
  21. if (requestInterceptors != null) {
  22. builder.requestInterceptors(requestInterceptors.values());
  23. }
  24. QueryMapEncoder queryMapEncoder = getOptional(context, QueryMapEncoder.class);
  25. if (queryMapEncoder != null) {
  26. builder.queryMapEncoder(queryMapEncoder);
  27. }
  28. if (this.decode404) {
  29. builder.decode404();
  30. }
  31. ExceptionPropagationPolicy exceptionPropagationPolicy = getOptional(context,
  32. ExceptionPropagationPolicy.class);
  33. if (exceptionPropagationPolicy != null) {
  34. builder.exceptionPropagationPolicy(exceptionPropagationPolicy);
  35. }
  36. }

FeignContext

这里存放的是各个feignclient的配置项,是通过自动配置类注册成bean的

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnClass(Feign.class)
  3. @EnableConfigurationProperties({ FeignClientProperties.class,
  4. FeignHttpClientProperties.class })
  5. @Import(DefaultGzipDecoderConfiguration.class)
  6. public class FeignAutoConfiguration {
  7. @Autowired(required = false)
  8. private List<FeignClientSpecification> configurations = new ArrayList<>();
  9. @Bean
  10. public HasFeatures feignFeature() {
  11. return HasFeatures.namedFeature("Feign", Feign.class);
  12. }
  13. @Bean
  14. public FeignContext feignContext() {
  15. FeignContext context = new FeignContext();
  16. //所有的配置
  17. context.setConfigurations(this.configurations);
  18. return context;
  19. }
  20. ....
  21. }

代理对象创建

  1. @Override
  2. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
  3. FeignContext context, Target.HardCodedTarget<T> target) {
  4. return feign.target(target);
  5. }
  6. public <T> T target(Target<T> target) {
  7. return build().newInstance(target);
  8. }
  1. public Feign build() {
  2. SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
  3. new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
  4. logLevel, decode404, closeAfterDecode, propagationPolicy);
  5. ParseHandlersByName handlersByName =
  6. new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
  7. errorDecoder, synchronousMethodHandlerFactory);
  8. return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
  9. }
  1. @Override
  2. public <T> T newInstance(Target<T> target) {
  3. Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  4. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  5. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  6. for (Method method : target.type().getMethods()) {
  7. if (method.getDeclaringClass() == Object.class) {
  8. continue;
  9. } else if (Util.isDefault(method)) {
  10. DefaultMethodHandler handler = new DefaultMethodHandler(method);
  11. defaultMethodHandlers.add(handler);
  12. methodToHandler.put(method, handler);
  13. } else {
  14. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  15. }
  16. }
  17. InvocationHandler handler = factory.create(target, methodToHandler);
  18. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
  19. new Class<?>[] {target.type()}, handler);
  20. for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  21. defaultMethodHandler.bindTo(proxy);
  22. }
  23. return proxy;
  24. }