一、读取注解信息

 入口

 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. 【转载】 xavier,kaiming初始化中的fan_in,fan_out在卷积神经网络是什么意思

    原文地址: https://www.cnblogs.com/liuzhan709/p/10092679.html =========================================== ...

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

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

  3. css 样式 element.style 覆盖问题

    问题: 我们在写网页定制样式的时候发现展示效果跟我们预想的不一样? 打开F12一看原来是element.style 覆盖的我定义的效果. 解决: 只要在定义的内容后面加上 !important 就行啦 ...

  4. div构建table

    1.Css display值与解释-(详细可见CSS手册的CSS display手册)参数:block :块对象的默认值.用该值为对象之后添加新行none :隐藏对象.与visibility属性的hi ...

  5. 关于腾讯会议pc端的使用教程

    一.首先先在官网下载pc端 下面是连接:腾讯会议_腾讯会议下载- 腾讯云 (tencent.com) 点击免费下载即可. 二.登录并创建会议 选择一种登录方式. 创建会议有快速会议和预定会议两种方式 ...

  6. Winform 子窗体调用父窗体方法

    子窗体部分 1.定义委托 /// <summary> /// 双击委托事件 /// </summary> /// <param name="path" ...

  7. Oracle同一台服务器创建多个数据库

    有时候我们需要再同一台机器上创建多个数据库服务(不是单纯的数据库实例),每一个数据库可以有单独的服务运行,只是在一个机器环境而已.可以在不同的端口上监听,也可以在相同端口监听 创建多个数据库步骤 安装 ...

  8. 使用JDBC查询数据库会一次性加载所有数据吗

    前几天有个小伙伴说他有个疑问:当我们发起一个查询的时候,数据库服务器是把所有结果集都准备好,然后一次性返回给应用程序服务吗(因为他们生产有个服务因为一个报表查询搞宕机了). 这样想的原因很简单,假设那 ...

  9. 手把手在STM32F103C8T6上构建可扩展可移植的DHT11驱动

    前言 如何驱动一个你陌生的传感器呢?别看我,也别在网上死马当活马医!你需要做的,首先是明确你的传感器的名称,在这里,我们想要使用的是DHT11温湿度传感器 可能需要的前置知识 简单的OLED驱动原理 ...

  10. JVM笔记六-堆区知识之对象生命周期和GC的关系

    通过上一篇文章的学习,我们对JVM堆区有了初步的认识,接下来,我们继续展开讲解堆区. 对象生命周期和GC的关系. 我们已经知道了,堆区的新生区分成了三个部分:伊甸园区.幸存者0区.幸存者1区. 其中0 ...