栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Feign详细构建过程及自定义扩展

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Feign详细构建过程及自定义扩展

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

spring-cloud-openfeign-core-2.1.1.RELEASE.jarHystrixFeign 的详细构建过程:

@EnableFeignClients -> FeignClientsRegistrar 扫描 @Feign注解的类 -> FeignClientFactoryBean通过Targeter生产FeignClient -> Targeter通过Feign.Builder构建Feign -> Feign.Builder

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 类型的 bean

  2. feign.hystrix.HystrixFeign类不存在时,使用 DefaultTargeter 。

  3. 看起来似乎可以使用自定义的Targeter代替Hystrix或默认的,这样就可以自定义各种功能了。实际上不行,因为 Targeter 是 package 访问级别的。

  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,并使用注解中的配置

  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。

class FeignClientFactoryBean
    implements FactoryBean, InitializingBean, ApplicationContextAware{
    //...
}

通过实现方法 FactoryBean#getObject()来由spring框架生产FeignClient。

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


 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<>(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);
5. 由于一开始注入的 *Targeter* 是 *HystrixTargeter* ,则此处是调用 HystrixTargeter 里的对应方法(从第一节的配置来看,只要 *feign.hystrix.HystrixFeign* 类存在,就是注入的 *HystrixTargeter *, 否则是 *DefaultTargeter*,对于需要**自定义构建feign的,这里不太重要**)
4.Targeter 4.1.HystrixTargeter
class HystrixTargeter implements Targeter {

	@Override
	public  T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget 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 targetWithFallbackFactory(String feignClientName, FeignContext context,
			Target.HardCodedTarget target, HystrixFeign.Builder builder,
			Class fallbackFactoryClass) {
		FallbackFactory fallbackFactory = (FallbackFactory) getFromContext(
				"fallbackFactory", feignClientName, context, fallbackFactoryClass,
				FallbackFactory.class);
		return builder.target(target, fallbackFactory);
	}

	private  T targetWithFallback(String feignClientName, FeignContext context,
			Target.HardCodedTarget 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 target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget 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 newInstance(Target target);
  
  public static class Builder {
    public  T target(Target 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 target(Target target, T fallback) {
      return build(fallback != null ? new FallbackFactory.Default(fallback) : null)
   .newInstance(target);
    }

    public  T target(Target target, FallbackFactory fallbackFactory) {
      return build(fallbackFactory).newInstance(target);
    }
      
    
    public  T target(Class apiType, String url, T fallback) {
      return target(new Target.HardCodedTarget(apiType, url), fallback);
    }

    public  T target(Class apiType,
   String url,
   FallbackFactory fallbackFactory) {
      return target(new Target.HardCodedTarget(apiType, url), fallbackFactory);
    }

    
    Feign build(final FallbackFactory nullableFallbackFactory) {
      super.invocationHandlerFactory(new InvocationHandlerFactory() {
 @Override
 public InvocationHandler create(Target target,
     Map 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
其中的 YiFeiXiInvocationHandler 和 YiFeiXiFeignFallbackFactory是自定义的。

@Override
public Feign build() {
    super.invocationHandlerFactory(new InvocationHandlerFactory() {
 @Override
 public InvocationHandler create(Target target,
     Map 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("&" + 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<>(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构建过程所用到的 Targeter 是 package 访问级别的,不能使用自定义的
  2. Feign以及Feign.Builder是 publilc,给了我们扩展的空间。
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号