大体流程鱼骨图

1.读取配置

 启动类上添加注解@EnableFeignClients,工程启动后会自动读取注解上的配置

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.TYPE)
3 @Documented
4 @Import(FeignClientsRegistrar.class)
5 public @interface EnableFeignClients {
6 。。。。。。属性省略
7 }

这里我们可以看到 EnableFeignClients 注解引入了配置类 org.springframework.cloud.openfeign.FeignClientsRegistrar

registerBeanDefinitions方法提供了注册 EnableFeignClients注解信息和 FeignClient信息的功能
1 @Override
2 public void registerBeanDefinitions(AnnotationMetadata metadata,
3 BeanDefinitionRegistry registry) {
4 registerDefaultConfiguration(metadata, registry);
5 registerFeignClients(metadata, registry);
6 }

拼接注解信息放在 definition中,然后交给spring容器管理

org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients

 1 private void registerFeignClient(BeanDefinitionRegistry registry,
2 AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
3 String className = annotationMetadata.getClassName();
4 //根据工厂类获得对象
5 BeanDefinitionBuilder definition = BeanDefinitionBuilder
6 .genericBeanDefinition(FeignClientFactoryBean.class);
7 validate(attributes);
8 definition.addPropertyValue("url", getUrl(attributes));
9 definition.addPropertyValue("path", getPath(attributes));
10 String name = getName(attributes);
11 definition.addPropertyValue("name", name);
12 String contextId = getContextId(attributes);
13 definition.addPropertyValue("contextId", contextId);
14 definition.addPropertyValue("type", className);
15 definition.addPropertyValue("decode404", attributes.get("decode404"));
16 definition.addPropertyValue("fallback", attributes.get("fallback"));
17 definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
18 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
19
20 String alias = contextId + "FeignClient";
21 AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
22
23 boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
24 // null
25
26 beanDefinition.setPrimary(primary);
27
28 String qualifier = getQualifier(attributes);
29 if (StringUtils.hasText(qualifier)) {
30 alias = qualifier;
31 }
32
33 BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
34 new String[] { alias });
35 BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
36 }

 

2.创建代理实例方法

feign.ReflectiveFeign#newInstance

 1   public <T> T newInstance(Target<T> target) {
2 Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
3 Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
4 List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
5 Method[] var5 = target.type().getMethods();
6 int var6 = var5.length;
7
8 for(int var7 = 0; var7 < var6; ++var7) {
9 Method method = var5[var7];
10 if (method.getDeclaringClass() != Object.class) {
11 if (Util.isDefault(method)) {
12 DefaultMethodHandler handler = new DefaultMethodHandler(method);
13 defaultMethodHandlers.add(handler);
14 methodToHandler.put(method, handler);
15 } else {
16 methodToHandler.put(method, (MethodHandler)nameToHandler.get(Feign.configKey(target.type(), method)));
17 }
18 }
19 }
20      //在这里我们可以看到feign创建了一个代理类
21 InvocationHandler handler = this.factory.create(target, methodToHandler);
22 T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
23 Iterator var12 = defaultMethodHandlers.iterator();
24
25 while(var12.hasNext()) {
26 DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
27 defaultMethodHandler.bindTo(proxy);
28 }
29
30 return proxy;
31 }

创建代理类的方法

1         public InvocationHandler create(Target target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
2 return new FeignInvocationHandler(target, dispatch);
3 }
4 }

代理类的invoke方法,feign.ReflectiveFeign.FeignInvocationHandler#invoke

 1  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
2 if (!"equals".equals(method.getName())) {
3 if ("hashCode".equals(method.getName())) {
4 return this.hashCode();
5 } else {
6 return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
7 }
8 } else {
9 try {
10 Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
11 return this.equals(otherHandler);
12 } catch (IllegalArgumentException var5) {
13 return false;
14 }
15 }
16 }

查看上面的invoke方法是怎么实现的

1     public interface MethodHandler {
2 Object invoke(Object[] var1) throws Throwable;
3 }

找到实现类 feign.SynchronousMethodHandler#invoke

 1  public Object invoke(Object[] argv) throws Throwable {
2 //这里创建了一个template
3 RequestTemplate template = this.buildTemplateFromArgs.create(argv);
4 Options options = this.findOptions(argv);
5 Retryer retryer = this.retryer.clone();
6
7 while(true) {
8 try {
9
10 return this.executeAndDecode(template, options);
11 } catch (RetryableException var9) {
12 RetryableException e = var9;
13
14 try {
15 retryer.continueOrPropagate(e);
16 } catch (RetryableException var8) {
17 Throwable cause = var8.getCause();
18 if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
19 throw cause;
20 }
21
22 throw var8;
23 }
24
25 if (this.logLevel != Level.NONE) {
26 this.logger.logRetry(this.metadata.configKey(), this.logLevel);
27 }
28 }
29 }
30 }

然后调用ribbon,执行rest请求 org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute

 1 @Override
2 public Response execute(Request request, Request.Options options) throws IOException {
3 try {
4 URI asUri = URI.create(request.url());
5 String clientName = asUri.getHost();
6 URI uriWithoutHost = cleanUrl(request.url(), clientName);
7 FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
8 this.delegate, request, uriWithoutHost);
9
10 IClientConfig requestConfig = getClientConfig(options, clientName);
11 return lbClient(clientName)
12 .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
13 }
14 catch (ClientException e) {
15 IOException io = findIOException(e);
16 if (io != null) {
17 throw io;
18 }
19 throw new RuntimeException(e);
20 }
21 }

-----------------------------------------------------------------------------------------------------------------------------

大部分都是贴的源码,本文只是为了记录一次feign的大致实现思想,其中很多精妙的方式并没有提及

spring cloud openfeign源码分析的更多相关文章

  1. Feign 系列(05)Spring Cloud OpenFeign 源码解析

    Feign 系列(05)Spring Cloud OpenFeign 源码解析 [TOC] Spring Cloud 系列目录(https://www.cnblogs.com/binarylei/p/ ...

  2. Spring Cloud Eureka源码分析 --- client 注册流程

    Eureka Client 是一个Java 客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的.使用轮询负载算法的负载均衡器. 在应用启动后,将会向Eureka Serve ...

  3. Spring Cloud Eureka源码分析之服务注册的流程与数据存储设计!

    Spring Cloud是一个生态,它提供了一套标准,这套标准可以通过不同的组件来实现,其中就包含服务注册/发现.熔断.负载均衡等,在spring-cloud-common这个包中,org.sprin ...

  4. 【spring cloud】源码分析(一)

    概述 从服务发现注解 @EnableDiscoveryClient入手,剖析整个服务发现与注册过程 一,spring-cloud-common包 针对服务发现,本jar包定义了 DiscoveryCl ...

  5. Spring Cloud Ribbon 源码分析---负载均衡算法

    上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...

  6. Spring Cloud Ribbon源码分析---负载均衡实现

    上一篇结合 Eureka 和 Ribbon 搭建了服务注册中心,利用Ribbon实现了可配置负载均衡的服务调用.这一篇我们来分析Ribbon实现负载均衡的过程. 从 @LoadBalanced入手 还 ...

  7. Spring Cloud Eureka源码分析---服务注册

    本篇我们着重分析Eureka服务端的逻辑实现,主要涉及到服务的注册流程分析. 在Eureka的服务治理中,会涉及到下面一些概念: 服务注册:Eureka Client会通过发送REST请求的方式向Eu ...

  8. Spring Cloud Eureka源码分析之三级缓存的设计原理及源码分析

    Eureka Server 为了提供响应效率,提供了两层的缓存结构,将 Eureka Client 所需要的注册信息,直接存储在缓存结构中,实现原理如下图所示. 第一层缓存:readOnlyCache ...

  9. Spring Cloud Eureka源码分析之心跳续约及自我保护机制

    Eureka-Server是如何判断一个服务不可用的? Eureka是通过心跳续约的方式来检查各个服务提供者的健康状态. 实际上,在判断服务不可用这个部分,会分为两块逻辑. Eureka-Server ...

  10. Spring Developer Tools 源码分析:二、类路径监控

    在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath ...

随机推荐

  1. 大语言模型(LLM)的逻辑推理能力的例子 —— 以ChatGPT3.5为例

    例子: PS. 不得不说,这个表现虽然没有那么完美但是已经比较惊艳了,比传统的自然语言对话系列的表现高出很多呀,很神奇,这个LLM的逻辑能力是如何实现的呢?虽然LLM不具备逻辑推理能力,但是LLM确实 ...

  2. ( Ubuntu环境下 )Vim插件推荐-Python自动补齐Vim插件jedi-vim的安装(使用插件管理器vundle进行安装)

    Ubuntu系统下,为 Vim 安装python自动补齐的插件   jedi-vim   . 1.   jedi-vim安装依赖 首先,jedi-vim插件需要当前Vim版本支持python,在终端输 ...

  3. 使用django-treebeard实现树类型存储与编辑

    前言 其实之前做很多项目都有遇到跟树相关的功能,以前都是自己实现的,然后前端很多UI组件库都有Tree组件,套上去就可以用. 不过既然用 Django 了,还是得充分发挥一下生态的优势,但是我找了半天 ...

  4. 微信支付java版(含视频讲解)

    1.背景 实际开发中用到微信支付的概率非常大, 至于为什么这里不必要我多少...... 微信支付大体需要对接的核心接口有 其实大部分支付都是这些,就像上一节我们讲的支付宝支付一样 这里以常用的H5支付 ...

  5. Floyd判联通(传递闭包) & poj1049 sorting it all out

    Floyd判联通(传递闭包) Floyd传递闭包顾名思义就是把判最短路的代码替换成了判是否连通的代码,它可以用来判断图中两点是否连通.板子大概是这个样的: for(int k=1; k<=n; ...

  6. TSP 的遗传算法

    省流:不如模拟退火 打 OI 的时候一直对乱搞很感兴趣,只是没时间学,现在算是弥补一下吧 旅行商问题(Traveling Salesman Problem, TSP):求无向图边权和最小的哈密顿回路 ...

  7. 可以调用Null的实例方法吗?

    前几天有个网友问我一个问题:调用实例方法的时候为什么目标对象不能为Null.看似一个简单的问题,还真不是一句话就能说清楚的.而且这个结论也不对,当我们调用定义在某个类型的实例时,目标对象其实可以为Nu ...

  8. 平衡搜索树-AVL树 图文详解 (万字长文)

    目录 AVL树 AVL树的概念 AVL树节点的定义: AVL树的插入 基本情况分析 平衡因子对应的操作 旋转操作 分析需要旋转的情况 结论 4种旋转操方法与特征 6种双旋平衡因子特征 代码实现 四种旋 ...

  9. 删除链表倒数第N个节点(19)

    双指针法 双指针法主要是最开始有两个指针fast,slow都指向链表的虚拟头节点dummy,然后快指针先移动,这里需要先向后移动n+1位(因为你最终是要找到目标节点的前一个节点),然后slow和fas ...

  10. ELK快速部署(踩坑记录、常见报错解决)及常用架构讲解

    ELK = Elasticserach + Logstash + kibana(包含但不仅限于) 简介: Elasticsearch:分布式搜索和分析引擎,具有高可伸缩.高可靠和易管理等特点.基于 A ...