Feign 主要是帮助我们方便进行rest api服务间的调用,其大体实现思路就我们通过标记注解在一个接口类上(注解上将包含要调用的接口信息),之后在调用时根据注解信息组装好请求信息,接下来基于ribbon这些负载均衡器来生成真实的服务地址,最后将请求发送出去;之后将接收到的结果反序列化为相关的Java对象供我们直接使用。 下面我们走进Spring Cloud对feign封装的源码中去了解其主要实现机制。

Feign的大体机制

通过在启动类上标记 @EnableFeignClients 注解来开启feign的功能,服务启动后会扫描 @FeignClient 注解标记的接口,然后根据扫描的注解信息为每个接口类生成feign客户端请求,同时解析接口方法中的Spring MVC的相关注解,通过专门的注解解析器识别这些注解信息,以便后面可以正确的组装请求参数,使用 Ribbon 和 Eureka 获取到请求服务的真实地址等信息,最后使用 http 相关组件进行执行调用。其大致流程图如下:

@EnableFeignClients 和 @FeignClient 注解

在EnableFeignClients 注解类中有一个 @Import(FeignClientsRegistrar.class)的配置

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 引入FeignClientsRegistrar 来扫描@FeignClient注解下的类
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
...
}

我们追踪代码进入到FeignClientsRegistrar类中,会发现FeignClientsRegistrar 类实现了ImportBeanDefinitionRegistrar(在spring context 项目中)接口,因此spring boot启动时会调用它的registerBeanDefinitions()方法,该方法中会扫描 EnableFeignClients 和 FeignClient 注解信息并设置相关信息。

/**
* spring boot 启动时会自动调用 ImportBeanDefinitionRegistrar 入口方法
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata
, BeanDefinitionRegistry registry) {
// 读取 @EnableFeignClients 注解中信息
registerDefaultConfiguration(metadata, registry);
// 扫描所有@FeignClient注解的类
registerFeignClients(metadata, registry);
}

registerDefaultConfiguration方法

在registerDefaultConfiguration()方法中会读取@EnableFeignClients注解信息,然后将这些信息注册到一个 BeanDefinitionRegistry 里面去;之后feign的一些默认配置将通过这里注册的信息中取获取。

registerFeignClients方法

  • registerFeignClients()方法会扫描相关包路径(如果EnableFeignClients的basePackages没有配置,默认会直接使用启动类所在的包路径)下所有的@FeiginClient注解的类
  • 然后根据@FeiginClient注解信息向BeanDefinitionRegistry里面注册bean,注意这里设置的bean名称生成规则是使用服务名+FeignClientSpecification.class.getSimpleName(),因此如果对一个服务写多个接口类会发生bean名称重复导致注册失败。所以需要增加一个 allow-bean-definition-overriding: true 的配置。
  • 最后会调用 registerFeignClient() 方法注册feign客户端,这里的bean名称的为当前接口类的类路径。

其流程图如下:

feign客户端的动态代理

上面registerFeignClient()方法中在构建bean的时候,实际构建的是FeignClientFactoryBean。

BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);

FeignClientFactoryBean 类对父类的getObject()方法进行了重写,后面动态代理时使用的就是它来获取feign client的。在这里会根据上面注解配置,同时会读取application.yml配置信息,根据配置来设置feign的相关信息,比如编解码器、注解解析器、请求超时时间等;之后如果没有设置url那么就会和负载均衡器(ribbon)整合。最后会通过反射将接口中相关方法进行解析保存供后面进行jdk代理使用。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断是否是不需要代理的
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// 需要代理,执行代理方法
return dispatch.get(method).invoke(args);
}

Spring-Cloud之Feign原理剖析的更多相关文章

  1. Spring Cloud 整合 Feign 的原理

    前言 在 上篇 介绍了 Feign 的核心实现原理,在文末也提到了会再介绍其和 Spring Cloud 的整合原理,Spring 具有很强的扩展性,会把一些常用的解决方案通过 starter 的方式 ...

  2. Spring Cloud OkHttp设计原理

    Spring Cloud 框架最底层核心的组件就是服务调用方式,一般Spring Cloud框架采用的是HTTP的调用框架,本文将在 Spring Cloud应用场景下,介绍组件OkHttp3的设计原 ...

  3. 撸一撸Spring Cloud Ribbon的原理-负载均衡器

    在上一篇<撸一撸Spring Cloud Ribbon的原理>中整理发现,RestTemplate内部调用负载均衡拦截器,拦截器内最终是调用了负载均衡器来选择服务实例. 接下来撸一撸负载均 ...

  4. 撸一撸Spring Cloud Ribbon的原理-负载均衡策略

    在前两篇<撸一撸Spring Cloud Ribbon的原理>,<撸一撸Spring Cloud Ribbon的原理-负载均衡器>中,整理了Ribbon如何通过负载均衡拦截器植 ...

  5. 0000 - Spring 中常用注解原理剖析

    1.概述 Spring 框架核心组件之一是 IOC,IOC 则管理 Bean 的创建和 Bean 之间的依赖注入,对于 Bean 的创建可以通过在 XML 里面使用 <bean/> 标签来 ...

  6. Spring 中常用注解原理剖析

    前言 Spring 框架核心组件之一是 IOC,IOC 则管理 Bean 的创建和 Bean 之间的依赖注入,对于 Bean 的创建可以通过在 XML 里面使用 <bean/> 标签来配置 ...

  7. spring cloud 使用feign 遇到问题

    spring cloud 使用feign 项目的搭建 在这里就不写了,本文主要讲解在使用过程中遇到的问题以及解决办法 1:示例 @RequestMapping(value = "/gener ...

  8. spring cloud(四) feign

    spring cloud 使用feign进行服务间调用 1. 新建boot工程 pom引入依赖 <dependency> <groupId>org.springframewor ...

  9. Eureka 系列(03)Spring Cloud 自动装配原理

    Eureka 系列(03)Spring Cloud 自动装配原理 [TOC] 0. Spring Cloud 系列目录 - Eureka 篇 本文主要是分析 Spring Cloud 是如何整合 Eu ...

  10. spring cloud关于feign client的调用对象列表参数、设置header参数、多环境动态参数试配

    spring cloud关于feign client的调用 1.有些场景接口参数需要传对象列表参数 2.有些场景接口设置设置权限等约定header参数 3.有些场景虽然用的是feign调用,但并不会走 ...

随机推荐

  1. OO_UNIT4_SUMMARY

    经过一个学期的学习,OO课程终于落下帷幕.本次博客会首先对第四单元作业的架构进行分析,再对OO课程进行总体回顾,最后是个人的建议与体会. 一.第四单元三次作业架构设计 1.第一次作业 第一次作业是对类 ...

  2. 书评第001篇:《C++黑客编程揭秘与防范》

    本书基本信息 作者:冀云(编著) 出版社:人民邮电出版社 出版时间:2012-6-1 ISBN:9787115280640 版次:1 页数:265 字数:406000 印刷时间:2012-6-1 开本 ...

  3. SSDT表结构的深入学习

    SSDT表的知识目录: A.了解SSDT结构 B.由SSDT索引号获取当前函数地址        C.如何获取索引号 D.获取起源地址-判断SSDT是否被HOOK E.如何向内核地址写入自己代码 A. ...

  4. hdu3786 Floyd或搜索 水题

    题意: 找出直系亲属 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  5. windows-CODE注入(远程线程注入)

    远程线程注入(先简单说,下面会详细说)今天整理下代码注入(远程线程注入),所谓代码注入,可以简单的理解为是在指定内进程里申请一块内存,然后把我们自己的执行代码和一些变量拷贝进去(通常是以启线程的方式) ...

  6. XCTF-web_python_template_injection

    web_python_template_injection 这里涉及到flask的ssti漏洞(服务端模板注入). 简单点说就是,在使用flask/jinja2的模板渲染函数render_templa ...

  7. (邹博ML)数学分析与概率论

    机器学习入门 深度学习和机器学习? 深度学习在某种意义上可以认为是机器学习的一个分支,只是这个分支非常全面且重要,以至于可以单独作为一门学科来进行研究. 回忆知识 求解S. 对数函数的上升速度 我们使 ...

  8. intellij idea的Maven项目运行报程序包找不到的错误

    概括一句话:IDEA的Terminal命令行输入mvn idea:idea或者mvn idea:module , 即可解决 重做过开发环境后在intellij idea中载入java工程,通过mave ...

  9. 关于调试器中int3断点引发异常的思考

    INT3断点 INT3断点是利用0Xcc指令实现的,cpu在执行0xcc指令时会引发断点异常调试器会捕捉这个异常. INT3断点引发的异常属于陷阱型异常,在执行完0xcc指令后eip指向下一条指令.但 ...

  10. RTTI之typeid运算符

    1 #include <iostream> 2 #include <cstdlib> 3 #include <ctime> 4 #include <typei ...