一、读取注解信息

 入口

 1 import org.springframework.boot.SpringApplication;
2 import org.springframework.boot.autoconfigure.SpringBootApplication;
3 import org.springframework.cloud.openfeign.EnableFeignClients;
4
5
6 @SpringBootApplication
7 @EnableFeignClients
8 public class CjsPriceServiceApplication {
9
10 public static void main(String[] args) {
11 SpringApplication.run(CjsPriceServiceApplication.class, args);
12 }
13
14 }

spring boot 项目启动后会自动扫描application上面的注解,@EnableFeignClients的注解如下

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

在注解中导入了 FeignClientsRegistrar类,用来像spring注册,EnableFeignClients和FeignClient上面开发人员添加的注解信息

1     @Override
2 public void registerBeanDefinitions(AnnotationMetadata metadata,
3 BeanDefinitionRegistry registry) {
4 registerDefaultConfiguration(metadata, registry);
5 registerFeignClients(metadata, registry);
6 }

二、当项目启动,读取@Autowired时会调用,实现了FactoryBean接口的FeignClientFactoryBean.getObject()方法

1     @Override
2 public Object getObject() throws Exception {
3 return getTarget();
4 }
 1 <T> T getTarget() {
2 FeignContext context = this.applicationContext.getBean(FeignContext.class);
3 Feign.Builder builder = feign(context);
4
5 if (!StringUtils.hasText(this.url)) {
6 if (!this.name.startsWith("http")) {
7 this.url = "http://" + this.name;
8 }
9 else {
10 this.url = this.name;
11 }
12 this.url += cleanPath();
13 return (T) loadBalance(builder, context,
14 new HardCodedTarget<>(this.type, this.name, this.url));
15 }
16 if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
17 this.url = "http://" + this.url;
18 }
19 String url = this.url + cleanPath();
20 Client client = getOptional(context, Client.class);
21 if (client != null) {
22 if (client instanceof LoadBalancerFeignClient) {
23 // not load balancing because we have a url,
24 // but ribbon is on the classpath, so unwrap
25 client = ((LoadBalancerFeignClient) client).getDelegate();
26 }
27 builder.client(client);
28 }
29 Targeter targeter = get(context, Targeter.class);
30 return (T) targeter.target(this, builder, context,
31 new HardCodedTarget<>(this.type, this.name, url));
32 }

可以看到 getTarget()有两种返回结果的情况,其原理都一样后来调用了 targeter.target()方法

 1 package org.springframework.cloud.openfeign;
2
3 import feign.Feign;
4 import feign.Target;
5
6 /**
7 * @author Spencer Gibb
8 */
9 interface Targeter {
10
11 <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
12 FeignContext context, Target.HardCodedTarget<T> target);
13
14 }

默认实现类

 1 package org.springframework.cloud.openfeign;
2
3 import feign.Feign;
4 import feign.Target;
5
6 /**
7 * @author Spencer Gibb
8 */
9 class DefaultTargeter implements Targeter {
10
11 @Override
12 public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
13 FeignContext context, Target.HardCodedTarget<T> target) {
14 return feign.target(target);
15 }
16
17 }

然后再看 feign.target(target);方法

 1     public <T> T target(Target<T> target) {
2 return build().newInstance(target);
3 }
4
5 public Feign build() {
6 SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
7 new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
8 logLevel, decode404, closeAfterDecode, propagationPolicy);
9 ParseHandlersByName handlersByName =
10 new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
11 errorDecoder, synchronousMethodHandlerFactory);
12 return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
13 }
14 }
build()方法返回了创建代理类的对象,然后调用了创建代理的 newInstance方法
 1   public <T> T newInstance(Target<T> target) {
2 Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
3 Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
4 List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
5
6 for (Method method : target.type().getMethods()) {
7 if (method.getDeclaringClass() == Object.class) {
8 continue;
9 } else if (Util.isDefault(method)) {
10 DefaultMethodHandler handler = new DefaultMethodHandler(method);
11 defaultMethodHandlers.add(handler);
12 methodToHandler.put(method, handler);
13 } else {
14 methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
15 }
16 }
17 InvocationHandler handler = factory.create(target, methodToHandler);
18 T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
19 new Class<?>[] {target.type()}, handler);
20
21 for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
22 defaultMethodHandler.bindTo(proxy);
23 }
24 return proxy;
25 }

最后,当我们项目中使用 @Autowired注入时,就回调用工厂类 FeignClientFactoryBean方法的 getObject()方法 返回我们的代理对象

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

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

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

  2. api网关揭秘--spring cloud gateway源码解析

    要想了解spring cloud gateway的源码,要熟悉spring webflux,我的上篇文章介绍了spring webflux. 1.gateway 和zuul对比 I am the au ...

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

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

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

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

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

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

  6. 【Day03】Spring cloud:源码讲解与容器化初探

    今日内容 原理和源码 容器化过度 一.Naocs 1.介绍 server端 启动入口类(Spring Boot项目,提供8848端口的监听访问) 源码包含InstanceController类(ser ...

  7. spring cloud ribbon源码解析(一)

    我们知道spring cloud中restTemplate可以通过服务名调接口,加入@loadBalanced标签就实现了负载均衡的功能,那么spring cloud内部是如何实现的呢? 通过@loa ...

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

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

  9. Spring Cloud Zuul源码

    一.Zuul源码分析(初始化流程.请求处理流程)

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

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

随机推荐

  1. 【FastDFS】06 SpringBoot实现上传

    创建SpringBoot工程: 再导入所需要的依赖: <dependency> <groupId>net.oschina.zcx7878</groupId> < ...

  2. 特斯拉在感知网络架构引入BEV三维空间转化层

    Learnging Where To Look End-to-End

  3. RTX显卡 运行TensorFlow=1.14.0 代码 报错 Could not create cudnn handle: CUDNN_STATUS_INTERNAL_ERROR

    硬件环境: RTX2070super 显卡 软件环境: Ubuntu18.04.5 Tensorflow = 1.14.0 -------------------------------------- ...

  4. 【转载】网络协议之:sctp流控制传输协议

    原文地址: https://www.cnblogs.com/flydean/p/16277006.html ============================================== ...

  5. Vue Vine:带给你全新的 Vue 书写体验!

    你好,我是 Kagol,个人公众号:前端开源星球. 上个月和 TinyVue 的小伙伴们一起参加了 VueConf 24 大会,有幸认识沈青川大佬,并了解了他的 Vue Vine 项目,Vue Vin ...

  6. WPF 使用MediaElement,但Source带https会抛出未引用对象实列

    原因和解决办法 如果恰好使用了MediaElement,在调用的时候,会出现空异常 var uri = new Uri("https://xxxx.mp4", UriKind.Re ...

  7. vue3:setup语法糖使用教程

    setup语法糖简介 直接在script标签中添加setup属性就可以直接使用setup语法糖了. 使用setup语法糖后,不用写setup函数:组件只需要引入不需要注册:属性和方法也不需要再返回,可 ...

  8. win10开启窗口左右分屏方法

    首先进入导航栏(等同于按下win徽标): 之后点击电源键上的"设置": 进入"系统" 左边一栏里点击"多任务处理" 打开"贴靠窗囗 ...

  9. Java jdk版本对照表

    这里将JDK版本和major.minor的版本的对照关系进行整理,作为今后查阅的依据. 序号 jdk版本 major.minor version 1 1.1 45 2 1.2 46 3 1.3 47 ...

  10. 017.Kubernetes二进制集群扩容worker

    一 前置准备 1.1 互信配置 为了更方便远程分发文件和执行命令,本实验配置master节点到其它节点的 ssh 信任关系. 1 [root@master01 ~]# ssh-copy-id -i ~ ...