Feign詳細構建過程及自定義擴展

探究清楚 feign 的原理,自定義 feign 功能html

spring-cloud-openfeign-core-2.1.1.RELEASE.jarHystrixFeign 的詳細構建過程:java

@EnableFeignClients -> FeignClientsRegistrar 掃描 @Feign註解的類 -> FeignClientFactoryBean經過Targeter生產FeignClient -> Targeter經過Feign.Builder構建Feign -> Feign.Builderspring

1. 準備工做(配置)

  1. 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();
		}

	}
  1. feign.hystrix.HystrixFeign類存在時,將 HystrixTargeter 註冊爲 Targeter 類型的 beanapi

  2. feign.hystrix.HystrixFeign類不存在時,使用 DefaultTargeterrestful

  3. 看起來彷佛可使用自定義的Targeter代替Hystrix或默認的,這樣就能夠自定義各類功能了。實際上不行,由於 Targeterpackage 訪問級別的。app

  4. FeignClientsConfiguration負載均衡

@Configuration
public class FeignClientsConfiguration {
    
            
    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer() {
        return Retryer.NEVER_RETRY;
    }

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Feign.Builder feignBuilder(Retryer retryer) {
        return Feign.builder().retryer(retryer);
    }
    
	@Configuration
	@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
	protected static class HystrixFeignConfiguration {
		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(name = "feign.hystrix.enabled")
		public Feign.Builder feignHystrixBuilder() {
			return HystrixFeign.builder();
		}
	}
}

重要Feign 以及內部類 Feign.Builder 都是 public 訪問級別,能夠注入自定義的bean。框架

2.EnableFeignClients與FeignClientsRegistrar類

將使用@FeignClient註解的類註冊成spring bean,並使用註解中的配置ide

  1. 在@EnableFeignClients註解中導入FeignClientsRegistrar類
  2. FeignClientsRegistrar類實現了ImportBeanDefinitionRegistrar類,會由spring框架執行實現方法 registerBeanDefinitions(AnnotationMetaData, BeanDefinitionRegistry)
  3. FeignClientsRegistrar中的 registerBeanDefinitions方法調用兩個方法
    1. registerDefaultConfiguration:註冊默認的配置
    2. registerFeignClients:註冊Feign客戶端(重點
  4. registerFeignClients:獲取 @EnableFeignClients註解中定義的配置掃描feign客戶端
  5. registerFeignClients:經過registerFeignClient(BeanDefinitionRegistry, AnnotationMetadata, Map)方法註冊每個feignClient,過程:先獲取 @FeignClient註解中定義的配置,將配置應用在spring bean 工廠 FeignClientFactoryBean, 經過工廠類 FeignClientFactoryBean 爲每個使用@FeignClient註解的類生產 FeignClient,詳細過程見下一節

3.FeignClientFactoryBean

FeignClient工廠bean。ui

class FeignClientFactoryBean
    implements FactoryBean<object>, InitializingBean, ApplicationContextAware{
    //...
}

經過實現方法 FactoryBean#getObject()來由spring框架生產FeignClient。

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

/**
 * 得到目標
 * 1. 得到FeignContext
 * 2. 從FeignContext中得到Feign構建器Feign.Builder
 * 3. 從FeignContext中得到Client,判斷是否進行負載均衡
 * 4. 從FeignContext中得到Target,並執行Target的默認方法target(FeignClientFactoryBean, Feign.Builder,
			FeignContext, Target.HardCodedTarget<t>);
 * 5.因爲一開始注入的Feign.Builder是HystrixFeign.Builder,則此處是調用HystrixFeign.Builder裏的對應方法
 */
<t> T getTarget() {
    FeignContext context = this.applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);
    //省略部分代碼
    // ......
    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&lt;&gt;(this.type, this.name, url));
}

	protected Feign.Builder feign(FeignContext context) {
		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
		Logger logger = loggerFactory.create(this.type);

		// @formatter:off
		Feign.Builder builder = get(context, Feign.Builder.class)
				// required values
				.logger(logger)
				.encoder(get(context, Encoder.class))
				.decoder(get(context, Decoder.class))
				.contract(get(context, Contract.class));
		// @formatter:on

		configureFeign(context, builder);

		return builder;
	}

工廠得到對象(目標):

1. 得到FeignContext(feign上下文)
2. 從FeignContext中得到Feign構建器Feign.Builder(public,能夠在此使用自定義構建器)
3. 從FeignContext中得到Client,判斷是否進行負載均衡
4. 從FeignContext中得到Target,並執行Target的默認方法target(FeignClientFactoryBean, Feign.Builder,
  FeignContext, Target.HardCodedTarget<t>);
5. 因爲一開始注入的 *Targeter* 是 *HystrixTargeter* ,則此處是調用 HystrixTargeter 裏的對應方法(從第一節的配置來看,只要 *feign.hystrix.HystrixFeign* 類存在,就是注入的 *HystrixTargeter *, 不然是 *DefaultTargeter*,對於須要**自定義構建feign的,這裏不過重要**)

4.Targeter

4.1.HystrixTargeter

class HystrixTargeter implements Targeter {

	@Override
	public <t> T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget<t> target) {
        // 若不是 HystrixFeign,則執行其對應的默認target方法。
        // 此處只處理HystrixFeign。
		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);
		}

        // 調用從Feign.Builder繼承的方法。
		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);
	}
    
    //...
}
  1. HystrixTarget只處理 Feign.Builder 類型爲 feign.hystrix.HystrixFeign.Builder
  2. 若feign構建器不是 feign.hystrix.HystrixFeign.Builder 類型,則執行注入的 feign 構建器的默認target方法
  3. 所以,即便注入的 Targeter 是 HystrixTargeter,此處也能夠執行自定義 Feign.Builder
  4. 理解:Feign.Builder#target(Target) 方法一般不會被 override(後續會講解爲何不重寫此方法)

4.2.DefaultTargeter

class DefaultTargeter implements Targeter {
	@Override
	public <t> T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget<t> target) {
		return feign.target(target);
	}
}
  1. 執行 Feign.Builder (子)類型對應的 默認 target方法。
  2. 理解:Feign.Builder#target(Target) 方法一般不會被 override(後續會講解爲何不重寫此方法)

5.FeignBuilder

feign構建器:構建feign對象。

> Feign的目的是將 http api 包裝成 restful 風格以便開發。 > > 在實現中,Feign 是一個爲目標http apis 生成 feign對象(Feign#newInstance)的工廠。

上述步驟目前須要的都是經過對應的 Builder 構建對應的Feign。

public abstract class Feign {

  public static Builder builder() {
    return new Builder();
  }
   
  public abstract <t> T newInstance(Target<t> target);
  
  public static class Builder {
    public <t> T target(Target<t> target) {
      return build().newInstance(target);
    }

    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);
    }
  }
}
  1. Feign.Builder#target(Target) 方法裏面實際上調用的是 build() 方法來構建對象,所以重寫 build() 方法便可,沒有必要還重寫 target(Target) 方法
  2. Feign 以及內部類 Feign.Builder 都是 public能夠重寫並注入自定義的bean

5.1.HystrixFeign

public final class HystrixFeign {
  public static final class Builder extends Feign.Builder {  
	@Override
    public Feign build() {
      return build(null);
    }
    
    // 提供一系列的target方法,支持各類配置:fallback、FallBackFactory等
    public <t> T target(Target<t> target, T fallback) {
      return build(fallback != null ? new FallbackFactory.Default<t>(fallback) : null)
          .newInstance(target);
    }

    public <t> T target(Target<t> target, FallbackFactory<!--? extends T--> fallbackFactory) {
      return build(fallbackFactory).newInstance(target);
    }
      
    
    public <t> T target(Class<t> apiType, String url, T fallback) {
      return target(new Target.HardCodedTarget<t>(apiType, url), fallback);
    }

    public <t> T target(Class<t> apiType,
                        String url,
                        FallbackFactory<!--? extends T--> fallbackFactory) {
      return target(new Target.HardCodedTarget<t>(apiType, url), fallbackFactory);
    }

    /** Configures components needed for hystrix integration. */
    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();
    }

基本到了這一步,須要設置的東西,均可以配置了。

  1. 雖然 build 方法中涉及到 InvocationHandler,但基本不須要改什麼,而 InvocationHandler 居然也是 package 訪問級別,因此只好複製一個,使用本身的。
  2. HystrixDelegatingContract 是 public 級別,不須要修改的話,仍然用這個。

5.2示例

如下示例參考 SentinelFeign
其中的 YiFeiXiInvocationHandlerYiFeiXiFeignFallbackFactory是自定義的。

@Override
public Feign build() {
    super.invocationHandlerFactory(new InvocationHandlerFactory() {
        @Override
        public InvocationHandler create(Target target,
                                        Map<method, methodhandler> dispatch) {
            // using reflect get fallback and fallbackFactory properties from
            // FeignClientFactoryBean because FeignClientFactoryBean is a package
            // level class, we can not use it in our package
            Object feignClientFactoryBean = Builder.this.applicationContext
                .getBean("&amp;" + target.type().getName());

            Class fallback = (Class) getFieldValue(feignClientFactoryBean,
                                                   "fallback");
            Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
                                                          "fallbackFactory");
            String name = (String) getFieldValue(feignClientFactoryBean, "name");

            Object fallbackInstance;
            FallbackFactory fallbackFactoryInstance;
            // check fallback and fallbackFactory properties
            // 如下邏輯在HystrixTargeter中有,但執行自定義的builder,不會執行到那段邏輯,所以此處加上。
            if (void.class != fallback) {
                fallbackInstance = getFromContext(name, "fallback", fallback,
                                                  target.type());
                return new YiFeiXiInvocationHandler(target, dispatch, setterFactory,
                                                    new FallbackFactory.Default(fallbackInstance));
            }
            if (void.class != fallbackFactory) {
                fallbackFactoryInstance = (FallbackFactory) getFromContext(name,
                                                                           "fallbackFactory", fallbackFactory,
                                                                           FallbackFactory.class);
                return new YiFeiXiInvocationHandler(target, dispatch, setterFactory,
                                                    fallbackFactoryInstance);
            }
            // 若註解中沒有使用fallback或fallbackFactory,則使用一個默認的FallbackFactory。
            return new YiFeiXiInvocationHandler(target, dispatch, setterFactory, new YiFeiXiFeignFallbackFactory&lt;&gt;(target));
        }

        private Object getFromContext(String name, String type,
                                      Class fallbackType, Class targetType) {
            Object fallbackInstance = feignContext.getInstance(name,
                                                               fallbackType);
            if (fallbackInstance == null) {
                throw new IllegalStateException(String.format(
                    "No %s instance of type %s found for feign client %s",
                    type, fallbackType, name));
            }

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

    super.contract(new HystrixDelegatingContract(contract));
    return super.build();
}

須要自定義fallbackFactory,則實現 feign.hystrix.FallbackFactory類,須要自定義fallback,則實現 org.springframework.cglib.proxy.MethodInterceptor便可

6.總結

  1. 因爲Feign構建過程所用到的 Targeterpackage 訪問級別的,不能使用自定義的
  2. Feign以及Feign.Builderpublilc,給了咱們擴展的空間。

7.參考資料

  1. feign-hystrix-10.1.0.jarspring-cloud-openfeign-core-2.1.1.RELEASE.jar
  2. spring-cloud-alibaba-sentinel-0.9.0.RELEASE.jar中的 sentinelFeign 實現
  3. Spring Cloud Alibaba Sentinel 整合 Feign 的設計實現</method,></method,></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></t></object>
相關文章
相關標籤/搜索