OpenFeign 架构原理(六):OpenFeign 动态代理原理

1,435 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

上次我们深入讲解了 Ribbon 的架构原理,这次我们再来看下 Feign 远程调用的架构原理。

上次我们已经梳理了 注册 FeignClient 到 Spring 的原理,接着我们就来顺着这个核心流程来讲解 OpenFeign 动态代理原理。

本文以开源 SpringCloud 项目 PassJava 作为示例。

开源地址: github.com/Jackson0714…

喜欢的小伙伴来点个 Star 吧,冲 2K Star。

上面的源码解析中我们也提到了是由这个工厂类 FeignClientFactoryBean 来创建 FeignCient Bean,所以我们有必要对这个类进行剖析。

在创建 FeignClient Bean 的过程中就会去生成动态代理对象。调用接口时,其实就是调用动态代理对象的方法来发起请求的。

分析动态代理的入口方法为 getObject()。源码如下所示:

Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
      new HardCodedTarget<>(this.type, this.name, url));

接着调用 target 方法这一块,里面的代码真的很多很细,我把核心的代码拿出来给大家讲下,这个 target 会有两种实现类:

DefaultTargeter 和 HystrixTargeter。而不论是哪种 target,都需要去调用 Feign.java 的 builder 方法去构造一个 feign client。

在构造的过程中,依赖 ReflectiveFeign 去构造。源码如下:

// 省略部分代码
public class ReflectiveFeign extends Feign {
  // 为 feign client 接口中的每个接口方法创建一个 methodHandler
  public <T> T newInstance(Target<T> target) {
    for(...) {
      methodToHandler.put(method, handler);
    }
    // 基于 JDK 动态代理的机制,创建了一个 passjava-study 接口的动态代理,所有对接口的调用都会被拦截,然后转交给 handler 的方法。
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
          new Class<?>[] {target.type()}, handler);
}

ReflectiveFeign 做的工作就是为带有 @FeignClient 注解的接口,创建出接口方法的动态代理对象。

比如示例代码中的接口 StudyTimeFeignService,会给这个接口中的方法 getMemberStudyTimeList 创建一个动态代理对象。

@FeignClient("passjava-study")
public interface StudyTimeFeignService {
    @RequestMapping("study/studytime/member/list/test/{id}")
    public R getMemberStudyTimeList(@PathVariable("id") Long id);
}

创建动态代理的原理图如下所示:

  • 解析 FeignClient 接口上各个方法级别的注解,比如远程接口的 URL、接口类型(Get、Post 等)、各个请求参数等。这里用到了 MVC Contract 协议解析,后面会讲到。
  • 然后将解析到的数据封装成元数据,并为每一个方法生成一个对应的 MethodHandler 类作为方法级别的代理。相当于把服务的请求地址、接口类型等都帮我们封装好了。这些 MethodHandler 方法会放到一个 HashMap 中。
  • 然后会生成一个 InvocationHandler 用来管理这个 hashMap,其中 Dispatch 指向这个 HashMap。
  • 然后使用 Java 的 JDK 原生的动态代理,实现了 FeignClient 接口的动态代理 Proxy 对象。这个 Proxy 会添加到 Spring 容器中。
  • 当要调用接口方法时,其实会调用动态代理 Proxy 对象的 methodHandler 来发送请求。

这个动态代理对象的结构如下所示,它包含了所有接口方法的 MethodHandler。

我是悟空,期待与你一起打怪升级变强,我们下期见。

作者简介:悟空,8年一线互联网开发和架构经验,用故事讲解分布式、架构设计、Java 核心技术。《JVM性能优化实战》专栏作者,开源了《Spring Cloud 实战 PassJava》项目,公众号:悟空聊架构。本文已收录至 www.passjava.cn