OpenFeign原理整理

OpenFeign

核心流程

1.在 Spring 项目启动阶段,服务 A 的OpenFeign 框架会发起一个主动的扫包流程。
2.从指定的目录下扫描并加载所有被 @FeignClient 注解修饰的接口,然后将这些接口转换成 Bean,统一交给 Spring 来管理。
3.根据这些接口会经过 MVC Contract 协议解析,将方法上的注解都解析出来,放到 MethodMetadata 元数据中。
4.基于上面加载的每一个 FeignClient 接口,会生成一个动态代理对象,指向了一个包含对应方法的 MethodHandler 的 HashMap。MethodHandler 对元数据有引用关系。生成的动态代理对象会被添加到 Spring 容器中,并注入到对应的服务里。
5.服务 A 调用接口,准备发起远程调用。
6.从动态代理对象 Proxy 中找到一个 MethodHandler 实例,生成 Request,包含有服务的请求 URL(不包含服务的 IP)。
7.经过负载均衡算法找到一个服务的 IP 地址,拼接出请求的 URL。
8.服务 B 处理服务 A 发起的远程调用请求,执行业务逻辑后,返回响应给服务 A。

OpenFeign 包扫描原理

@EnableFeignClients 这个注解使用 Spring 框架的 Import 注解导入了 FeignClientsRegistrar 类

// 启动类加上这个注解
@EnableFeignClients(basePackages = "com.test.feign")
 
// EnableFeignClients 类还引入了 FeignClientsRegistrar 类
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
  ...
}

FeignClientsRegistrar 负责 Feign 接口的加载。

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
   // 注册配置
   registerDefaultConfiguration(metadata, registry);
   // 注册 FeignClient
   registerFeignClients(metadata, registry);
}

registerFeignClients 会扫描指定包。

Set<BeanDefinition> candidateComponents = scanner
      .findCandidateComponents(basePackage);

只保留带有 @FeignClient 的接口。

// 判断是否是带有注解的 Bean。
if (candidateComponent instanceof AnnotatedBeanDefinition) {
  // 判断是否是接口
   AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
   AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
  // @FeignClient 只能指定在接口上。
   Assert.isTrue(annotationMetadata.isInterface(),
         "@FeignClient can only be specified on an interface");
注册 FeignClient 到 Spring 的原理

        1.解析 @FeignClient 定义的属性。
        2.将注解@FeignClient 的属性 + 接口 StudyTimeFeignService的信息构造成一个 StudyTimeFeignService 的 beanDefinition。
        3.然后将 beanDefinition 转换成一个 holder,这个 holder 就是包含了 beanDefinition, alias, beanName 信息。
        4.最后将这个 holder 注册到 Spring 容器中。

OpenFeign 动态代理原理
// 省略部分代码
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 注解的接口,创建出接口方法的动态代理对象。

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

OpenFeign 如何与 Ribbon 整合的原理

        1.根据服务名从缓存中找 FeignLoadBalancer,如果缓存中没有,则创建一个 FeignLoadBalancer。
        2.FeignLoadBalancer 会创建出一个 command,这个 command 会执行一个 sumbit 方法。
        3.submit 方法里面就会用 Ribbon 的负载均衡算法选择一个 server。
        4.然后将 IP 地址和之前剔除掉服务名称的 URL 进行拼接,生成最后的服务地址。
        5.最后 FeignLoadBalancer 执行 execute 方法发送请求。

OpenFeign 处理响应的原理

这个里面做的事情就是调用 ResponseEntityDecoder 的 decode 方法,将 Json 字符串转化为 Bean 对象。

<think>嗯,用户问的是Feign和OpenFeign的区别以及各自的原理。首先,我得回忆一下这两个东西到底是什么。Feign好像是一个Netflix开发的声明式的HTTP客户端,用来简化服务之间的调用。而OpenFeign是不是后来由Spring Cloud整合的那个版本?或者是另一个分支? 记得Feign最初是Netflix OSS的一部分,后来Netflix停止维护,可能被贡献给开源社区,而Spring Cloud整合了Feign,并可能进行了扩展,形成了OpenFeign?或者是不是OpenFeign其实是Feign的一个开源版本,而原来的Feign已经不再更新了? 需要确认两者的来源。Feign是Netflix开发的,而OpenFeign可能是在Spring Cloud生态中的实现,或者是一个独立的项目?或者可能OpenFeign是Feign的社区版,继续维护?或者两者其实是同一个东西,只是名称上的差异? 另外,用户提到各自的原理。Feign的原理应该是基于注解和动态代理,通过接口定义和注解来描述HTTP请求,然后生成实现类,发送请求。而OpenFeign可能是在Feign的基础上整合了Spring的功能,比如和Ribbon、Eureka等组件结合,实现负载均衡和服务发现。 需要进一步查证两者的具体区别。比如,OpenFeign是否支持Spring MVC的注解,而Feign可能不支持?或者Feign需要手动集成其他组件,而OpenFeign自动集成? 还有版本历史方面,可能Feign在某个时间点停止维护,OpenFeign作为分支继续发展,或者被Spring Cloud吸收成为其一部分。比如,Spring Cloud OpenFeign项目是不是基于Feign的封装? 总结可能的区别: 1. 来源和维护:Feign由Netflix开发,可能已经停止维护;OpenFeignSpring社区维护,集成在Spring Cloud中。 2. 功能扩展:OpenFeign可能增加了对Spring MVC注解的支持,比如@GetMapping、@PathVariable等,而原生Feign可能使用自己的注解。 3. 集成能力:OpenFeignSpring Cloud的其他组件如Eureka、Ribbon、Hystrix等无缝集成,提供负载均衡和服务发现,而原生Feign需要手动配置。 4. 依赖和生态:Feign可能更轻量,适合非Spring项目;而OpenFeign专为Spring Cloud设计,依赖Spring生态系统。 关于原理部分,两者的核心机制应该类似,都是通过动态代理生成接口实现,将注解转化为HTTP请求。但OpenFeign可能在此基础上利用Spring的ApplicationContext和RestTemplate等组件,增强功能。 需要确认这些信息是否准确,可能参考官方文档或者可靠的技术资料。比如,Spring Cloud OpenFeign的文档会说明它是基于Feign的,同时扩展了Spring的支持。 另外,用户可能在使用Spring Cloud时接触到的是OpenFeign,所以需要强调两者的关系,以及为什么会有OpenFeign的存在,比如为了更好的与Spring生态整合,提供更多便利功能。 最后,整理回答结构,分点说明区别,然后分别解释两者的工作原理,确保逻辑清晰,用户能理解它们的不同应用场景和选择依据。</think>### Feign 和 OpenFeign 的区别及原理 #### 一、核心区别 1. **来源与维护** - **Feign**:由 Netflix 开发,最初作为 Netflix OSS 的一部分,用于简化 HTTP API 调用。 - **OpenFeign**:是 Spring Cloud 社区基于原生 Feign 的增强版本,整合到 Spring Cloud 生态中,持续维护并扩展功能。 2. **注解支持** - **Feign**:使用原生注解(如 `@RequestLine`, `@Param`),语法独立于 Spring。 - **OpenFeign**:支持 Spring MVC 注解(如 `@GetMapping`, `@PathVariable`),与 Spring Boot 无缝兼容。 3. **集成能力** - **Feign**:需手动集成负载均衡(如 Ribbon)、服务发现(如 Eureka)。 - **OpenFeign**:自动集成 Spring Cloud 组件(如 Ribbon、Eureka、Hystrix),开箱即用。 4. **适用场景** - **Feign**:轻量级,适合非 Spring 项目。 - **OpenFeign**:专为 Spring Cloud 微服务设计,依赖 Spring 生态。 --- #### 二、工作原理 ##### 1. Feign 的核心原理 - **动态代理**:通过 JDK 动态代理生成接口实现类,将注解解析为 HTTP 请求模板。 - **注解翻译**:例如 `@RequestLine("GET /user/{id}")` 会被解析为 HTTP 方法和路径。 - **请求发送**:结合编码器(Encoder)、解码器(Decoder)和 HTTP 客户端(如 OkHttp)发送请求。 ##### 2. OpenFeign 的增强机制 - **Spring 整合**:利用 Spring 的 `ApplicationContext` 和 `RestTemplate`,自动注入 Bean。 - **负载均衡**:通过 Ribbon 实现服务实例选择,支持轮询、随机等策略。 - **服务发现**:集成 Eureka,动态获取服务实例列表。 - **注解兼容**:将 Spring MVC 注解(如 `@RequestMapping`)映射为 HTTP 请求,降低学习成本。 --- #### 三、代码示例对比 **原生 Feign 接口定义** ```java public interface UserService { @RequestLine("GET /user/{id}") User getUser(@Param("id") String id); } ``` **OpenFeign 接口定义(Spring 风格)** ```java @FeignClient(name = "user-service") public interface UserService { @GetMapping("/user/{id}") User getUser(@PathVariable("id") String id); } ``` --- #### 四、总结 - **选型建议**: - 若项目基于 Spring Cloud,优先选择 **OpenFeign**(无需重复造轮子)。 - 若为轻量级非 Spring 项目,可使用原生 **Feign**。 - **核心价值**:两者均通过 **声明式接口** 屏蔽 HTTP 调用细节,但 OpenFeign 在易用性和生态整合上更胜一筹。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码蒋

您的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值