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

**spring-cloud-openfeign-core-2.1.1.RELEASE.jar** 中 **HystrixFeign** 的详细构建过程:

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

# 1. 准备工作(配置)

1. FeignAutoConfiguration自动配置类

```java
@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** 访问级别的。

2. FeignClientsConfiguration

```java
@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。

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

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

```java
@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);
* 5.由于一开始注入的Feign.Builder是HystrixFeign.Builder,则此处是调用HystrixFeign.Builder里的对应方法
*/
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

```java
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

```java
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。

```java
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

```java
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);
}

/** Configures components needed for hystrix integration. */
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`是自定义的。
```java
@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*,给了我们扩展的空间。

# 7.参考资料

1. **feign-hystrix-10.1.0.jar**和**spring-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 的设计实现](https://www.cnblogs.com/javastack/p/10129409.html)

Feign详细构建过程及自定义扩展的更多相关文章

  1. Gradle的构建过程都不会?带你全面了解Android如何自定义Gradle 插件

    目前 Android 工程的默认构建工具为 Gradle,我们在构建 APK 的时候往往会执行 ./gradlew assembleDebug 这样的命令.. 那么这个命令到底代表着什么含义呢?命令的 ...

  2. WCF自定义扩展,以实现aop!

    引用地址:https://msdn.microsoft.com/zh-cn/magazine/cc163302.aspx  使用自定义行为扩展 WCF Aaron Skonnard 代码下载位置: S ...

  3. spring boot actuator端点高级进阶metris指标详解、git配置详解、自定义扩展详解

    https://www.cnblogs.com/duanxz/p/3508267.html 前言 接着上一篇<Springboot Actuator之一:执行器Actuator入门介绍>a ...

  4. 源码解析.Net中Host主机的构建过程

    前言 本篇文章着重讲一下在.Net中Host主机的构建过程,依旧延续之前文章的思路,着重讲解其源码,如果有不知道有哪些用法的同学可以点击这里,废话不多说,咱们直接进入正题 Host构建过程 下图是我自 ...

  5. Mysql主从复制,读写分离(mysql-proxy),双主结构完整构建过程

    下面介绍MySQL主从复制,读写分离,双主结构完整构建过程,不涉及过多理论,只有实验和配置的过程. Mysql主从复制(转载请注明出处,博文地址:) 原理是master将改变记录到二进制日志(bina ...

  6. Silverlight实例教程 - 自定义扩展Validation类,验证框架的总结和建议(转载)

    Silverlight 4 Validation验证实例系列 Silverlight实例教程 - Validation数据验证开篇 Silverlight实例教程 - Validation数据验证基础 ...

  7. vue 源码学习(一) 目录结构和构建过程简介

    Flow vue框架使用了Flow作为类型检查,来保证项目的可读性和维护性.vue.js的主目录下有Flow的配置.flowconfig文件,还有flow目录,指定了各种自定义类型. 在学习源码前可以 ...

  8. 第十三节:HttpHander扩展及应用(自定义扩展名、图片防盗链)

    一. 自定义扩展名 1. 前言 凡是实现了IHttpHandler接口的类均为Handler类,HttpHandler是一个HTTP请求的真正处理中心,在HttpHandler容器中,ASP.NET ...

  9. android构建过程

    参考: http://blog.csdn.net/shangmingchao/article/details/47375111 首先,需要了解一下构建APK的七大工具: ①aapt 全称是Androi ...

随机推荐

  1. MFC在一个工程中启动其他工程的exe文件

    说明:有的时候把两个工程合并,但是偷懒不想在工程中添加代码,所以想到了这个办法,仅限偷懒哈哈哈哈 方法:新建一个主程序,在主程序的界面中添加按钮,在按钮的程序代码中添加以下语句: void CMain ...

  2. 记录一次pycharm中,引入其他类可用,下面总是有波浪线,而且Ctrl+b 无法查看类函数的源码

    最近在玩python,发现引入其他的函数们总是有波浪线,但是能够使用,crtl+b却无法看到,非常尴尬,然后查看了原因,记录如下: This inspection detects names that ...

  3. 新手小白之学习python一飞冲天日志之—基本数据类型,条件控制语句

    python的历史 04年目前最流行的WEB框架Django诞生 python2:源码不统一,有重复的功能代码 python3:源码统一,没有重复的功能代码 python是一个什么编程语言 编译型:编 ...

  4. C#5.0新增功能02 调用方信息

    连载目录    [已更新最新开发文章,点击查看详细] 通过使用调用方信息特性,可获取有关方法的调用方的信息. 可以获取源代码的文件路径.源代码中的行号和调用方的成员名称. 此信息有助于跟踪.调试和创建 ...

  5. C# MATLAB混编(一)

    参照这篇博客进行的C# MATLAB混编学习,学习过程中文章中的一些问题我并没有遇到,但是我遇到了一些新问题,这些问题的解决办法将在下一篇博客给出. 配置环境:vs2010(64位)+Matlab20 ...

  6. java练习---2

    //程序员:罗元昊 2017.9.6public class My{       public static void main(String[] args){              int a= ...

  7. chapter02 - 03

    1.分别用cat \tac\nl三个命令查看文件/etc/ssh/sshd_config文件中的内容,并用自己的话总计出这三个文档操作命令的不同之处? 答:cat /etc/ssh/sshd_conf ...

  8. Linux零拷贝技术,看完这篇文章就懂了

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫. 本文讲解 ...

  9. 重入锁的学习 (ReentrantLock)

    重入锁  :(ReentrantLock) 上锁 用reentrantLock.lock 方法 解锁 用reentrantLock.unlock 方法 上锁和解锁 必须配对 可以多重上锁 Reentr ...

  10. S2:c#继承

    在C#中,如果一个类后面通过冒号又跟了另外一个类,那么我们就称冒号前面的类为子类,冒号后面的类为父类.这种书写类的方式放映出来的关系就称为类的继承关系. 1.子类:派生类 父类:基类或者超类 满足is ...