盘点 Cloud : FeignBean 的创建及代理

1,036 阅读5分钟

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

总文档 :文章目录
Github : github.com/black-ant

一 . 前言

前面一篇说了Feign 的初始化配置 , 这一篇来说说 FeingBean 的加载 :

接上一篇中 , FeignClient 被扫描处理 , 加入 Bean 工厂中

BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

那么后续是如何使用的呢?

二 . FeignBean 的创建

Bean 的创建主要是基于 FeignClientFactoryBean , 通过工厂实现具体的 FeignBean 个体 ,后续接口代理的时候 , 会反射到该类

2.1 发起的原因

  • Step 1 : Spring 加载一个 Bean ,发现 Bean 中有一个 Autowired 标注的对象
  • Step 2 : 通过 AbstractBeanFactory getBean 获取这对象
  • Step 3 : getSingleton 查询到对应的 工厂方法 (FeignClientFactoryBean)
  • Step 4 : 调用 FeignClientFactoryBean # getObject 获取对象
  • Step 5 : ReflectiveFeign 初始化绑定对象 (newInstance)
    • Step 5.1 : ReflectiveFeign.apply(Target target) 对 Method 进行处理
    • Step 5.1 : ReflectiveFeign.apply(Target target) 对 Method 进行处理

简单点说 ,当出现 Autowired 注入 FeginClient 的时候 , 会通过 FeignClientFactoryBean 返回一个代理类

2.2 调用的流程

前置知识点 : FactoryBean 发起 Object 的获取

PS : IOC 容器中会构建 FactoryBean , 在需要实现 Object 的时候 , 调用 FactoryBean 的 getObject 方法获取具体的Object 对象

C07- FactoryBean
    M- T getObject() throws Exception
    M- Class<?> getObjectType()
    M- default boolean isSingleton()
    
// PS : 此处 FeignClientFactoryBean 实现了该方法
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware
    

扩展 : 了解过 IOC 流程的应该会比较清楚 , 这里简单点说就是提供一个方法 ,让 IOC 获取 FactoryBean 需要创建的类的实例 (即从 FactoryBean 获取它工厂处理出来的对象)

相关逻辑可以从 DefaultSingletonBeanRegistry # getSingleton 方法中看到

com.gang.cloud.template.demo.client.SampleFeignClient 
-> FeignClientFactoryBean{
type=interface com.gang.cloud.template.demo.client.SampleFeignClient, 
name='nacos-account-server', 
url='http://nacos-account-server', 
path='', 
decode404=false, 
inheritParentContext=true, 
applicationContext=     
    org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@4eb1c69, 
    started on Tue May 18 15:47:20 CST 2021, 
    parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@4cc61eb1,
fallback=void, 
fallbackFactory=void}

Step 1 : FeignClientFactoryBean 调用主流程

主调流程为 :

  • FeignClientFactoryBean.getObject
  • FeignClientFactoryBean.getTarget
  • FeignClientFactoryBean.loadBalance
  • HystrixTargeter.target
  • ReflectiveFeign.newInstance :

这里看到 , 这里的起点是 getObject , 该方法是一个重写方法 , 源头为接口 FactoryBean < Object >

C09- FeignClientFactoryBean
    M09_01- getTarget
        1- this.applicationContext.getBean(FeignContext.class) : 获取Feign 核心容器
        2- 通过容器配置获取 Feign.Builder 对象
        3- 最终调用 loadBalance 进行负载均衡的封装


<T> T getTarget() {
    // 1 . 准备了 FeignContext
    FeignContext context = this.applicationContext.getBean(FeignContext.class);
    // 2 . 准备了 Feign.Builder
    Feign.Builder builder = feign(context);

    // 
    if (!StringUtils.hasText(this.url)) {
        if (!this.name.startsWith("http")) {
            this.url = "http://" + this.name;
        } else {
            this.url = this.name;
        }
        this.url += cleanPath();
        // 通常情况下 , URL 不为空 , 会总结返回 loadBalance 对象
        return (T) loadBalance(builder, context,
            new HardCodedTarget<>(this.type, this.name, this.url));
    }
    if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
        this.url = "http://" + this.url;
    }
    String url = this.url + cleanPath();
    // 此处获得连接的 client 对象
    Client client = getOptional(context, Client.class);
    if (client != null) {
        if (client instanceof LoadBalancerFeignClient) {
            client = ((LoadBalancerFeignClient) client).getDelegate();
        }
        if (client instanceof FeignBlockingLoadBalancerClient) {
            client = ((FeignBlockingLoadBalancerClient) 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));
}



[Pro0001] : FeignContext 的作用 ? 创建虚类实例的工厂。它为每个客户端名称创建一个Spring ApplicationContext,并从中提取所需的bean.

[Pro0002] : Feign.Builder ?

这是一个 feign 构造工具 , 包含常用的配置信息和工具对象 , 例如最常见的 client 和 加密解密工具 , 可以理解为一个集合体

public static class Builder {

    private final List<RequestInterceptor> requestInterceptors =
        new ArrayList<RequestInterceptor>();
    private Logger.Level logLevel = Logger.Level.NONE;
    private Contract contract = new Contract.Default();
    private Client client = new Client.Default(null, null);
    private Retryer retryer = new Retryer.Default();
    private Logger logger = new NoOpLogger();
    private Encoder encoder = new Encoder.Default();
    private Decoder decoder = new Decoder.Default();
    private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
    private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
    private Options options = new Options();
    private InvocationHandlerFactory invocationHandlerFactory =
        new InvocationHandlerFactory.Default();
    private boolean decode404;
    private boolean closeAfterDecode = true;
    private ExceptionPropagationPolicy propagationPolicy = NONE;
    private boolean forceDecoding = false;
    private List<Capability> capabilities = new ArrayList<>();
    
}

Step 2 : loadBalance 构建

C09- FeignClientFactoryBean
    M09_02- loadBalance
        1- feign.Client 对象创建 (LoadBalancerFeignClient)
        2- Targeter 创建 (HystrixTargeter)
        3- targeter.target 处理 (包括 fallback , fallbackFactory , SetterFactory) 
        
// PS : 在上一个步骤中 , 返回了一个 loadBalance 对象 >>>


protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
    // LoadBalancerFeignClient         
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        // HystrixTargeter
        Targeter targeter = get(context, Targeter.class);
        
        return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
        "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
        
}


// Targeter 是一个接口 , 主要有2个实现类 : HystrixTargeter , DefaultTargeter


}




[Pro] : getOptional 获取 Client

此处会根据我们之前初始化中说的 , 通过配置选择是否调用 OkHttpClient 或者 HttpClient , 配置后就会发现 , 实际上进入了如下类 :

C- OkHttpFeignConfiguration
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
    ConnectionPool connectionPool,
    FeignHttpClientProperties httpClientProperties) {
    
    //......................
    
}

Step 3 : HystrixTargeter 的处理

因为之前的 Feign 就行 HystrixFeign ,所以通常这里会直接调用 feign.target(target);

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;
    String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
        : factory.getContextId();
    SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
    if (setterFactory != null) {
        builder.setterFactory(setterFactory);
    }
    Class<?> fallback = factory.getFallback();
    if (fallback != void.class) {
        return targetWithFallback(name, context, target, builder, fallback);
    }
    Class<?> fallbackFactory = factory.getFallbackFactory();
    if (fallbackFactory != void.class) {
    return targetWithFallbackFactory(name, context, target, builder,fallbackFactory);
    }

    return feign.target(target);
}

PS : 如果不是 HystrixFeign , 后续其实还是创建了一个 HystrixFeign

Step 4 : Invoke 代理类的构建

C30- ReflectiveFeign
    M30_01- apply(Target target)
        1- 获取 Method 元数据集合 List<MethodMetadata>  -> PS:M30_01_01
        2- 根据类型不同构建 BuildTemplateByResolvingArgs -> PS:M30_01_02
        3- 构建 MethodHandler -> PS:M30_01_03
     M30_02- newInstance(Target<T> target) -> PS:M30_02_03
        1- 获取所有方法的 Map<String, MethodHandler> 集合 nameToHandler
        // 对象准备    
        2- Map<Method, MethodHandler> methodToHandler -> LV:M30_02_01
        3- List<DefaultMethodHandler> defaultMethodHandlers
        // For 循环处理
        4-FOR- nameToHandler (Map<String, MethodHandler>)
            - methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)))
                 ?- 添加到  LV:M30_02_01 methodToHandler 中
        // 实体类构建
        5- new ReflectiveFeign.FeignInvocationHandler(target, dispatch) : 构建代理对象 InvocationHandler
        6- Proxy.newProxyInstance : 构建代理类  核心方法
            
            
// M30_01 源代码
public Map<String, MethodHandler> apply(Target target) {
      List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate =
              new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
        }
        if (md.isIgnored()) {
          result.put(md.configKey(), args -> {
            throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
          });
        } else {
          //  构建 MethodHandler
          result.put(md.configKey(),
              // 此处的 factory 为 SynchronousMethodHandler.Factory (静态内部类)       
              factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
        }
      }
      return result;
    }
}


// M30_02 源代码
public <T> T newInstance(Target<T> target) {
    // 注意 , 此处直接调用了 apply
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply , 获得代理类(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 核心逻辑 , 代理对象的生成
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}

PS:M30_01_01 元数据集合

Feign_client_method_meatadata.jpg

PS:M30_01_02 BuildTemplateByResolvingArgs 对象

Feign_client_build_template.jpg

PS:M30_01_03 MethodHandler 对象参数

可以看到 , 相关的参数均在里面了

feign_method_handler.jpg

总结

这一部分也不麻烦 ,总得来说就是生成了一个 Proxy ,对接口进行了代理