首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Feign 实现原理 5 步曲

Feign 实现原理 5 步曲

原创
作者头像
叶子--
发布2025-09-21 23:55:05
发布2025-09-21 23:55:05
12200
代码可运行
举报
运行总次数:0
代码可运行

一、Feign 实现原理 5 步曲

  1. 解析接口 → 把 @FeignClient 接口上所有注解拼成「元数据」
  2. 动态代理 → 为接口生成 JDK Proxy,InvocationHandler 是 Feign 自带的 ReflectiveFeign.FeignInvocationHandler
  3. 契约解析 → 把 SpringMVC 注解(@GetMapping、@RequestParam…)转成 Feign 内部的「MethodMetadata」
  4. 编解码 → 根据接口方法参数/返回值,找到对应的 Encoder/Decoder(默认 SpringEncoder / ResponseEntityDecoder)
  5. 拦截执行 → 把 MethodMetadata → RequestTemplate → Target → Client(Apache/OkHttp/LoadBalancerClient)真正发 HTTP

二、每一步对应的核心代码

下面代码均来自 openfeign-core 11.xspring-cloud-openfeign 3.x,只保留“能说明原理”的最小片段。

  1. 解析接口
代码语言:javascript
代码运行次数:0
运行
复制
// SpringCloud 启动时,@EnableFeignClients 会 import FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(...) {
        // 扫描 basePackage,拿到所有 @FeignClient 接口
        for (BeanDefinition candidate : scanner.findCandidateComponents(basePackage)) {
            String className = candidate.getBeanClassName();
            // 给每一个接口注册一个 FactoryBean:FeignClientFactoryBean
            BeanDefinitionBuilder def = BeanDefinitionBuilder
                    .genericBeanDefinition(FeignClientFactoryBean.class);
            def.addPropertyValue("type", ClassUtils.forName(className, null));
            registry.registerBeanDefinition("feign." + simpleName, def.getBeanDefinition());
        }
    }
}
  1. 动态代理入口
代码语言:javascript
代码运行次数:0
运行
复制
// FeignClientFactoryBean.getObject() 被 Spring 调用来创建代理
public Object getObject() throws Exception {
    return feign().target(Targeter.target(this.type, this.name, this.url));
}
  1. 契约解析(SpringMVC → Feign)
代码语言:javascript
代码运行次数:0
运行
复制
// SpringMvcContract 把 @GetMapping 等转成 MethodMetadata
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
    MethodMetadata md = new MethodMetadata();
    // 1. 解析路径
    String path = processAnnotationOnClass(targetType);
    path = processAnnotationOnMethod(method, path);
    md.template().insert(0, path);
    // 2. 解析参数
    for (Parameter parameter : method.getParameters()) {
        if (parameter.getAnnotation(RequestParam.class) != null) {
            String name = parameter.getAnnotation(RequestParam.class).value();
            md.indexToName().put(i, name);
        }
    }
    return md;
}
  1. 编解码
代码语言:javascript
代码运行次数:0
运行
复制
// SpringEncoder 把 @RequestBody Object 转成 HttpOutputMessage
public void encode(Object requestBody, Type bodyType, RequestTemplate template) {
    HttpMessageConverter converter = selectConverter(requestBody, bodyType);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    HttpOutputMessage msg = new HttpOutputMessage(out);
    converter.write(requestBody, contentType, msg);
    template.body(out.toByteArray(), Charset.forName("UTF-8"));
}
  1. 拦截执行(真正发请求)
代码语言:javascript
代码运行次数:0
运行
复制
// SynchronousMethodHandler 是 InvocationHandler 的“方法级”实现
public Object invoke(Object[] argv) {
    // 1. 把 MethodMetadata + 实参 → RequestTemplate
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 2. 应用 RequestInterceptor(加 header、日志)
    for (RequestInterceptor interceptor : requestInterceptors) {
        interceptor.apply(template);
    }
    // 3. 转成 Request 并执行
    Request request = targetRequest(template);
    Response response = client.execute(request, options);
    // 4. 解码
    return decode(response);
}

三、FeignClientFactoryBean 是什么

  1. 本质:一个 Spring FactoryBean Spring 在启动时给每个 @FeignClient 接口注册的不是接口本身,而是 FeignClientFactoryBean 的 BeanDefinition。 当业务代码第一次 @Autowired FooService 时,Spring 会调用 FeignClientFactoryBean#getObject() 创建真正的代理对象。
  2. 职责
    • 持有接口类型、serviceId、url、path、fallback 等全部 @FeignClient 属性
    • 根据这些属性拼装 Feign.Builder(编码器、解码器、契约、client、拦截器、logger)
    • 调用 Feign.Builder.target() 生成 JDK 动态代理,然后注册到 Spring 容器
  3. 生命周期
代码语言:javascript
代码运行次数:0
运行
复制
@EnableFeignClients
└─ FeignClientsRegistrar 扫描接口
   └─ 注册 BeanDefinition(beanClass = FeignClientFactoryBean)
      └─ Spring 依赖注入阶段
         └─ FeignClientFactoryBean#getObject()
            └─ Feign.Builder → Targeter → Proxy
               └─ 返回代理对象给 Spring 容器

一句话总结

Feign 的原理就是“注解解析 + 动态代理 + 模板发请求”; FeignClientFactoryBean 是 Spring-Cloud 把 Feign 接入 Spring IOC 的“工厂”,负责把接口变成可注入的 Bean。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、每一步对应的核心代码
  • 三、FeignClientFactoryBean 是什么
  • 一句话总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档