前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >FeignClient源码深度解析

FeignClient源码深度解析

作者头像
方志朋
发布2019-06-21 11:10:12
2.8K0
发布2019-06-21 11:10:12
举报
文章被收录于专栏:史上最简单的Spring Cloud教程

本文转载于微信公众号:吉姆餐厅ak 学习更多源码知识,欢迎关注。 全文共16984字左右。


概述

springCloud feign主要对netflix feign进行了增强和包装,本篇从源码角度带你过一遍装配流程,揭开feign底层的神秘面纱。 主要包括feign整合ribbon,hystrix,sleuth,以及生成的代理类最终注入到spring容器的过程。篇幅略长,耐心读完,相信你会有所收获。


Feign架构图

一些核心类及大致流程:

大体步骤: 一、注册FeignClient配置类和FeignClient BeanDefinition 二、实例化Feign上下文对象FeignContext 三、创建 Feign.builder 对象 四、生成负载均衡代理类 五、生成默认代理类 六、注入到spring容器


源码分析

主要围绕上面6个步骤详细分析。


一、注册FeignClient配置类和FeignClient BeanDefinition

从启动类注解开始,来看下 @EnableFeignClients注解:

  1. @EnableFeignClients
  2. public class MyApplication {
  3. }

这是在启动类开启feign装配的注解,跟进该注解,看看做了什么:

  1. @Import(FeignClientsRegistrar.class)
  2. public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
  3. `ResourceLoaderAware, BeanClassLoaderAware {`
  4. `// patterned after Spring Integration IntegrationComponentScanRegistrar`
  5. `// and RibbonClientsConfigurationRegistgrar`
  6. `private final Logger logger = LoggerFactory.getLogger(FeignClientsRegistrar.class);`
  7. `private ResourceLoader resourceLoader;`
  8. `private ClassLoader classLoader;`
  9. `public FeignClientsRegistrar() {`
  10. `}`
  11. `@Override`
  12. `public void setResourceLoader(ResourceLoader resourceLoader) {`
  13. `this.resourceLoader = resourceLoader;`
  14. `}`
  15. `@Override`
  16. `public void setBeanClassLoader(ClassLoader classLoader) {`
  17. `this.classLoader = classLoader;`
  18. `}`
  19. `@Override`
  20. `public void registerBeanDefinitions(AnnotationMetadata metadata,`
  21. `BeanDefinitionRegistry registry) {`
  22. `//1、先注册默认配置`
  23. `registerDefaultConfiguration(metadata, registry);`
  24. `//2、注册所有的feignClient beanDefinition`
  25. `registerFeignClients(metadata, registry);`
  26. `}`
  27. `//...`
  28. }

我们分别来看一下上面 registerBeanDefinitions**中的两个方法:** 1) 注册默认配置方法: registerDefaultConfiguration

  1. `private void registerDefaultConfiguration(AnnotationMetadata metadata,`
  2. `BeanDefinitionRegistry registry) {`
  3. `Map<String, Object> defaultAttrs = metadata`
  4. `.getAnnotationAttributes(EnableFeignClients.class.getName(), true);`
  5. `if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {`
  6. `String name;`
  7. `if (metadata.hasEnclosingClass()) {`
  8. `name = "default." + metadata.getEnclosingClassName();`
  9. `}`
  10. `else {`
  11. `name = "default." + metadata.getClassName();`
  12. `}`
  13. `// name 默认以 default 开头,后续会根据名称选择配置`
  14. `registerClientConfiguration(registry, name,`
  15. `defaultAttrs.get("defaultConfiguration"));`
  16. `}`
  17. `}`

上述方法为读取启动类上面 @EnableFeignClients注解中声明feign相关配置类,默认name为default,一般情况下无需配置。用默认的 FeignAutoConfiguration即可。 上面有个比较重要的方法:注册配置 registerClientConfiguration,启动流程一共有两处读取feign的配置类,这是第一处。根据该方法看一下

  1. `private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,`
  2. `Object configuration) {`
  3. `BeanDefinitionBuilder builder = BeanDefinitionBuilder`
  4. `.genericBeanDefinition(FeignClientSpecification.class);`
  5. `builder.addConstructorArgValue(name);`
  6. `builder.addConstructorArgValue(configuration);`
  7. `registry.registerBeanDefinition(`
  8. `name + "." + FeignClientSpecification.class.getSimpleName(),`
  9. `builder.getBeanDefinition());`
  10. `}`

上面将bean配置类包装成 FeignClientSpecification,注入到容器。该对象非常重要,包含FeignClient需要的重试策略,超时策略,日志等配置,如果某个服务没有设置,则读取默认的配置。

2、扫描FeignClient

该方法主要是扫描类路径,对所有的FeignClient生成对应的 BeanDefinitio

  1. public void registerFeignClients(AnnotationMetadata metadata,
  2. `BeanDefinitionRegistry registry) {`
  3. `//...`
  4. `//获取扫描目录下面所有的bean deanDefinition`
  5. `for (String basePackage : basePackages) {`
  6. `Set<BeanDefinition> candidateComponents = scanner`
  7. `.findCandidateComponents(basePackage);`
  8. `for (BeanDefinition candidateComponent : candidateComponents) {`
  9. `if (candidateComponent instanceof AnnotatedBeanDefinition) {`
  10. `// verify annotated class is an interface`
  11. `AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;`
  12. `AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();`
  13. `Assert.isTrue(annotationMetadata.isInterface(),`
  14. `"@FeignClient can only be specified on an interface");`
  15. `Map<String, Object> attributes = annotationMetadata`
  16. `.getAnnotationAttributes(`
  17. `FeignClient.class.getCanonicalName());`
  18. `String name = getClientName(attributes);`
  19. `//这里是第二处`
  20. `registerClientConfiguration(registry, name,`
  21. `attributes.get("configuration"));`
  22. `//注册feignClient`
  23. `registerFeignClient(registry, annotationMetadata, attributes);`
  24. `}`
  25. `}`
  26. `}`
  27. `}`

可以看到上面又调用了 registerClientConfiguration**注册配置的方法,这里是第二处调用。这里主要是将扫描的目录下,每个项目的配置类加载的容器当中。** 注册到容器中,什么时候会用到呢?具体又如何使用呢?别着急,后面会有介绍。

我们先会回到继续主流程,继续看注册feignClient的方法,跟进 registerFeignClient

  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. `//logger.info("TEX do some replacement");`
  8. `//attributes.put("value", ((String)attributes.get("value")).replace('_','-'));`
  9. `validate(attributes);`
  10. `definition.addPropertyValue("url", getUrl(attributes));`
  11. `definition.addPropertyValue("path", getPath(attributes));`
  12. `String name = getName(attributes);`
  13. `definition.addPropertyValue("name", name);`
  14. `definition.addPropertyValue("type", className);`
  15. `definition.addPropertyValue("decode404", attributes.get("decode404"));`
  16. `definition.addPropertyValue("fallback", attributes.get("fallback"));`
  17. `definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);`
  18. `String alias = name + "FeignClient";`
  19. `AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();`
  20. `beanDefinition.setPrimary(true);`
  21. `BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,`
  22. `new String[] { alias });`
  23. `//将bean definition加入到spring容器`
  24. `BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);`
  25. `}`

划重点,上面出现了一行相当关键代码

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

springCloud FeignClient其实是利用了spring的代理工厂来生成代理类,所以这里将所有的 feignClient的描述信息 BeanDefinition设定为 FeignClientFactoryBean类型,该类又继承 FactoryBean,很明显,这是一个代理类。 在spring中, FactoryBean是一个工厂bean,用作创建代理bean,所以得出结论,feign将所有的feignClient bean包装成 FeignClientFactoryBean。扫描方法到此结束。

代理类什么时候会触发生成呢? 在spring刷新容器时,当实例化我们的业务service时,如果发现注册了FeignClient,spring就会去实例化该FeignClient,同时会进行判断是否是代理bean,如果为代理bean,则调用 FeignClientFactoryBean**的** T getObject() throws Exception;**方法生成代理bean。**


先来隆重介绍一下 FeignClientFactoryBean,后面四步都基于此类。

先看一下代理feignClient代理生成入口: getObject方法

  1. @Override
  2. `public Object getObject() throws Exception {`
  3. `// 二、实例化Feign上下文对象FeignContext`
  4. `FeignContext context = applicationContext.getBean(FeignContext.class);`
  5. `// 三、生成builder对象,用来生成feign`
  6. `Feign.Builder builder = feign(context);`
  7. `// 判断生成的代理对象类型,如果url为空,则走负载均衡,生成有负载均衡功能的代理类`
  8. `if (!StringUtils.hasText(this.url)) {`
  9. `String url;`
  10. `if (!this.name.startsWith("http")) {`
  11. `url = "http://" + this.name;`
  12. `}`
  13. `else {`
  14. `url = this.name;`
  15. `}`
  16. `url += cleanPath();`
  17. `// 四、生成负载均衡代理类`
  18. `return loadBalance(builder, context, new HardCodedTarget<>(this.type,`
  19. `this.name, url));`
  20. `}`
  21. `//如果指定了url,则生成默认的代理类`
  22. `if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {`
  23. `this.url = "http://" + this.url;`
  24. `}`
  25. `String url = this.url + cleanPath();`
  26. `// 五、生成默认代理类`
  27. `return targeter.target(this, builder, context, new HardCodedTarget<>(`
  28. `this.type, this.name, url));`
  29. `}`

getObject()逻辑比较多,每一行都会做一些初始化配置,来逐步分析。

二、实例化Feign上下文对象FeignContext

上述方法中第一行便是实例化 FeignContext

  1. FeignContext context = applicationContext.getBean(FeignContext.class);

获取 FeignContext对象,如果没有实例化,则主动实例化,如下:

  1. @Configuration
  2. @ConditionalOnClass(Feign.class)
  3. public class FeignAutoConfiguration {
  4. `@Autowired(required = false)`
  5. `private List<FeignClientSpecification> configurations = new ArrayList<>();`
  6. `@Bean`
  7. `public HasFeatures feignFeature() {`
  8. `return HasFeatures.namedFeature("Feign", Feign.class);`
  9. `}`
  10. `@Bean`
  11. `public FeignContext feignContext() {`
  12. `FeignContext context = new FeignContext();`
  13. `//将feign的配置类设置到feign的容器当中`
  14. `context.setConfigurations(this.configurations);`
  15. `return context;`
  16. `}`
  17. }

可以看到feign的配置类设置到feign的容器当中,而集合中的元素 正是上面我们提到的两处调用 registerClientConfiguration方法添加进去的,前后呼应。

然而,当我们引入了 sleuth之后,获取的 feignContext确是 TraceFeignClientAutoConfiguration中配置的实例 sleuthFeignContext:

可以看到上面创建了一个 TraceFeignContext实例,因为该对象继承 FeignContext,同时又加了 @Primary注解,所以在上面第2步中通过类型获取: applicationContext.getBean(FeignContext.class);,最终拿到的是 TraceFeignContext


三、构造 FeignBuilder

继续跟进该方法:

Feign.Builder builder = feign(context);

  1. protected Feign.Builder feign(FeignContext context) {
  2. `Logger logger = getOptional(context, Logger.class);`
  3. `if (logger == null) {`
  4. `logger = new Slf4jLogger(this.type);`
  5. `}`
  6. `// 1、构造 Feign.Builder`
  7. `Feign.Builder builder = get(context, Feign.Builder.class)`
  8. `// required values`
  9. `.logger(logger)`
  10. `.encoder(get(context, Encoder.class))`
  11. `.decoder(get(context, Decoder.class))`
  12. `.contract(get(context, Contract.class));`
  13. `// 2、设置重试策略,log等组件`
  14. `//设置log级别`
  15. `Logger.Level level = getOptional(context, Logger.Level.class);`
  16. `if (level != null) {`
  17. `builder.logLevel(level);`
  18. `}`
  19. `//设置重试策略`
  20. `Retryer retryer = getOptional(context, Retryer.class);`
  21. `if (retryer != null) {`
  22. `builder.retryer(retryer);`
  23. `}`
  24. `//feign的错误code解析接口`
  25. `ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);`
  26. `if (errorDecoder != null) {`
  27. `builder.errorDecoder(errorDecoder);`
  28. `}`
  29. `//超时时间设置,连接超时时间:connectTimeout默认10s,请求请求超时时间:readTimeout默认60s`
  30. `Request.Options options = getOptional(context, Request.Options.class);`
  31. `if (options != null) {`
  32. `builder.options(options);`
  33. `}`
  34. `//拦截器设置,可以看出拦截器也是可以针对单独的feignClient设置`
  35. `Map<String, RequestInterceptor> requestInterceptors = context.getInstances(`
  36. `this.name, RequestInterceptor.class);`
  37. `if (requestInterceptors != null) {`
  38. `builder.requestInterceptors(requestInterceptors.values());`
  39. `}`
  40. `if (decode404) {`
  41. `builder.decode404();`
  42. `}`
  43. `return builder;`
  44. `}`

上述代码有两处逻辑,分别来看:

1、 Feign.Builder builder = get(context, Feign.Builder.class) ,又会有以下三种情况:

1)单独使用Feign,没有引入 sleuthhystrix: 通过加载FeignClientsConfiguration的配置创建 Feign的静态内部类: Feign.Builder

  1. `@Bean`
  2. `@Scope("prototype")`
  3. `@ConditionalOnMissingBean`
  4. `public Feign.Builder feignBuilder(Retryer retryer) {`
  5. `return Feign.builder().retryer(retryer);`
  6. `}`

2)引入了 hystrix,没有引入 sleuth: 通过加载 FeignClientsConfiguration的配置创建 HystrixFeign的静态内部类: HystrixFeign.Builder

  1. `@Configuration`
  2. `@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })`
  3. `protected static class HystrixFeignConfiguration {`
  4. `@Bean`
  5. `@Scope("prototype")`
  6. `@ConditionalOnMissingBean`
  7. `@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = false)`
  8. `public Feign.Builder feignHystrixBuilder() {`
  9. `return HystrixFeign.builder();`
  10. `}`
  11. `}`

3)同时引入 hystrixsleuth: 加载 TraceFeignClientAutoConfiguration的配置创建: HystrixFeign.Builder

注意:

  • TraceFeignClientAutoConfiguration的配置类加载一定是在 FeignClientsConfiguration之前(先加载先生效),而 FeignClientsConfiguration加载是通过 FeignAutoConfiguration完成的,所以上图中引入了条件注解:
代码语言:txt
复制
1.  `@AutoConfigureBefore({FeignAutoConfiguration.class})`
  • 创建创建的 builder对象和第二种情况一下,只是做了一层包装
  1. final class SleuthFeignBuilder {
  2. `private SleuthFeignBuilder() {}`
  3. `static Feign.Builder builder(Tracer tracer, HttpTraceKeysInjector keysInjector) {`
  4. `return HystrixFeign.builder()`
  5. `//各组件&grave;client,retryer,decoder&grave;进行增强,装饰器模式。`
  6. `.client(new TraceFeignClient(tracer, keysInjector))`
  7. `.retryer(new TraceFeignRetryer(tracer))`
  8. `.decoder(new TraceFeignDecoder(tracer))`
  9. `.errorDecoder(new TraceFeignErrorDecoder(tracer));`
  10. `}`
  11. }

2、设置重试策略,log等组件 Feign.builder在获取之后又分别指定了重试策略,日志级别,错误代码code等,在上一步中调用 SleuthFeignBuilder.build()时已经设置过默认值了,这里为什么要重复设置呢?

我们跟进去get()方法,一探究竟:

  1. `protected <T> T get(FeignContext context, Class<T> type) {`
  2. `//根据name,也就是服务名称来生成builder`
  3. `T instance = context.getInstance(this.name, type);`
  4. `if (instance == null) {`
  5. `throw new IllegalStateException("No bean found of type " + type + " for "`
  6. `+ this.name);`
  7. `}`
  8. `return instance;`
  9. `}`
  10. `public <T> T getInstance(String name, Class<T> type) {`
  11. `//这里获取AnnotationConfigApplicationContext容器`
  12. `AnnotationConfigApplicationContext context = getContext(name);`
  13. `if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,`
  14. `type).length > 0) {`
  15. `return context.getBean(type);`
  16. `}`
  17. `return null;`
  18. `}`
  19. `private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();`
  20. `protected AnnotationConfigApplicationContext getContext(String name) {`
  21. `if (!this.contexts.containsKey(name)) {`
  22. `synchronized (this.contexts) {`
  23. `if (!this.contexts.containsKey(name)) {`
  24. `//这里创建容器createContext(name)`
  25. `this.contexts.put(name, createContext(name));`
  26. `}`
  27. `}`
  28. `}`
  29. `return this.contexts.get(name);`
  30. `}`

重点来了,上述代码将FeignContext做了缓存,每个服务对应一个FeignContext,服务名作为key。 继续跟进 createContext(name)方法:

  1. protected AnnotationConfigApplicationContext createContext(String name) {
  2. `//注意:这里的容器并不是spring的容器,而是每次都重新创建一个`
  3. `AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();`
  4. `//加载每个服务对应的配置类`
  5. `if (this.configurations.containsKey(name)) {`
  6. `for (Class<?> configuration : this.configurations.get(name)`
  7. `.getConfiguration()) {`
  8. `context.register(configuration);`
  9. `}`
  10. `}`
  11. `//加载启动类@EnableFeignClients注解指定的配置类`
  12. `for (Map.Entry<String, C> entry : this.configurations.entrySet()) {`
  13. `if (entry.getKey().startsWith("default.")) {`
  14. `for (Class<?> configuration : entry.getValue().getConfiguration()) {`
  15. `context.register(configuration);`
  16. `}`
  17. `}`
  18. `}`
  19. `//注册默认的配置类:FeignClientsConfiguration`
  20. `context.register(PropertyPlaceholderAutoConfiguration.class,`
  21. `this.defaultConfigType);`
  22. `context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(`
  23. `this.propertySourceName,`
  24. `Collections.<String, Object> singletonMap(this.propertyName, name)));`
  25. `if (this.parent != null) {`
  26. `// Uses Environment from parent as well as beans`
  27. `context.setParent(this.parent);`
  28. `}`
  29. `//刷新容器`
  30. `context.refresh();`
  31. `return context;`
  32. `}`

可以看到上述AnnotationConfigApplicationContext容器并非spring容器,只是利用了spring刷新容器的方法来实例化配置类,以服务名作为key,配置隔离。

重点来了,上面加载配置的顺序为:先加载每个服务的配置类,然后加载启动类注解上的配置类,最后加载默认的配置类。这样做有什么好处? spring刷新容器的方法也是对所有的bean进行了缓存,如果已经创建,则不再实例化。所以优先选取每个FeignClient的配置类,最后默认的配置类兜底。

所以这也证明了 sleuth的配置一定在 feign的配置类之前加载。 至此, FeignBuilder构造流程结束。


四、生成负载均衡代理类

再贴一下生成代理类的入口:

  1. `//判断url是否为空`
  2. `if (!StringUtils.hasText(this.url)) {`
  3. `//......`
  4. `return loadBalance(builder, context, new HardCodedTarget<>(this.type,`
  5. `this.name, url));`
  6. `}`
  7. `//......`
  8. `return targeter.target(this, builder, context, new HardCodedTarget<>(`
  9. `this.type, this.name, url));`

这里有个重要判断:判断FeignClient声明的url是否为空,来判断具体要生成的代理类。如下: 这么做有什么意义? 1)如果为空,则默认走Ribbon代理,也就是这个入口,会有加载ribbon的处理。 @FeignClient("MyFeignClient") 2)如果不为空,指定url,则走默认生成代理类的方式,也就是所谓的硬编码。 @FeignClient(value = "MyFeignClient",url = "http://localhost:8081") 这样处理方便开发人员进行测试,无需关注注册中心,直接http调用,是个不错的开发小技巧。

生产环境也可以用上述第二种方式,指定域名的方式。

我们跟进 loadBalance方法:

  1. `protected <T> T loadBalance(Feign.Builder builder, FeignContext context,`
  2. `HardCodedTarget<T> target) {`
  3. `//获得FeignClient`
  4. `Client client = getOptional(context, Client.class);`
  5. `if (client != null) {`
  6. `builder.client(client);`
  7. `return targeter.target(this, builder, context, target);`
  8. `}`
  9. `throw new IllegalStateException(`
  10. `"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-ribbon?");`
  11. `}`

Client client = getOptional(context, Client.class);这里会从 FeignContext上下文中获取 Client对象,该对象有三种实例,具体是哪个实现呢?

这里又会有三种情况: 1)没有整合 ribbonsleuth: 获取默认的 ClientDefault实例。

2)整合了 ribbon,没有整合 sleuth: 获取 LoadBalanceFeignClient实例。

3)整合了 ribbonsleuth: 会获取 TraceFeignClient实例,该实例是对 LoadBalanceFeignClient的一种包装,实现方式通过 BeanPostProcessor实现: FeignBeanPostProcessor中定义了包装逻辑:

  1. `@Override`
  2. `public Object postProcessBeforeInitialization(Object bean, String beanName)`
  3. `throws BeansException {`
  4. `return this.traceFeignObjectWrapper.wrap(bean);`
  5. `}`

通过 wrap方法最终返回 TraceFeignClient实例。

继续回到主流程,先来看下 Targeter接口:

  1. interface Targeter {
  2. `<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,`
  3. `HardCodedTarget<T> target);`
  4. `}`

该对象定义在 FeignClientFactoryBean静静态代码块中:

  1. `private static final Targeter targeter;`
  2. `static {`
  3. `Targeter targeterToUse;`
  4. `//判断类路径是否引入了hystrixFeign`
  5. `if (ClassUtils.isPresent("feign.hystrix.HystrixFeign",`
  6. `FeignClientFactoryBean.class.getClassLoader())) {`
  7. `targeterToUse = new HystrixTargeter();`
  8. `}`
  9. `else {`
  10. `targeterToUse = new DefaultTargeter();`
  11. `}`
  12. `targeter = targeterToUse;`
  13. `}`

这里会初始化 Targeter,该类是生成feign代理类的工具类,有两种实现,正是上面的 HystrixTargeter, DefaultTargeter。 因为我们引入了 hystrix,所以 Targeter实现类为 HystrixTargeter。我们继续跟进 targeter.target方法:

  1. public <T> T target(Target<T> target) {
  2. `return build().newInstance(target);`
  3. `}`

上面通过 build()方法获取生成代理类的工具类 ReflectiveFeign,再通过 newInstance正式创建代理类。 继续跟进

  1. `public Feign build() {`
  2. `SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =`
  3. `new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,`
  4. `logLevel, decode404);`
  5. `ParseHandlersByName handlersByName =`
  6. `new ParseHandlersByName(contract, options, encoder, decoder,`
  7. `errorDecoder, synchronousMethodHandlerFactory);`
  8. `return new ReflectiveFeign(handlersByName, invocationHandlerFactory);`
  9. `}`

这里会创建Feign的方法工厂 synchronousMethodHandlerFactory, Feign通过该工厂为每个方法创建一个 methodHandler,每个 methodHandler中包含Feign对应的配置: retryerrequestInterceptors等。

继续跟进 newInstance方法:

  1. public <T> T newInstance(Target<T> target) {
  2. `//创建所有的 MethodHandler`
  3. `Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);`
  4. `Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();`
  5. `List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();`
  6. `for (Method method : target.type().getMethods()) {`
  7. `if (method.getDeclaringClass() == Object.class) {`
  8. `continue;`
  9. `//判断是否启用默认handler`
  10. `} else if(Util.isDefault(method)) {`
  11. `DefaultMethodHandler handler = new DefaultMethodHandler(method);`
  12. `defaultMethodHandlers.add(handler);`
  13. `methodToHandler.put(method, handler);`
  14. `} else {`
  15. `methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));`
  16. `}`
  17. `}`
  18. `//创建InvocationHandler,接收请求,转发到methodHandler`
  19. `InvocationHandler handler = factory.create(target, methodToHandler);`
  20. `//生成代理类`
  21. `T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);`
  22. //将默认方法绑定到代理类
  23. `for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {`
  24. `defaultMethodHandler.bindTo(proxy);`
  25. `}`
  26. `return proxy;`
  27. }

InvocationHandler最终创建的实例为 HystrixInvocationHandler,核心方法如下:

  1. HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setter) {
  2. `@Override`
  3. `protected Object run() throws Exception {`
  4. `try {`
  5. `return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);`
  6. `} catch (Exception e) {`
  7. `throw e;`
  8. `} catch (Throwable t) {`
  9. `throw (Error) t;`
  10. `}`
  11. `}`
  12. `@Override`
  13. `protected Object getFallback() {`
  14. `//......`
  15. `}`
  16. `};`

整个流程:Feign调用方发起请求,发送至hystrix的HystrixInvocationHandler,通过服务名称,找到对应方法的methodHandler,methodHandler中封装了loadBalanceClient、retryer、RequestInterceptor等组件,如果引入了sleuth,这几个组件均是sleuth的包装类。然后通过以上组件构造 http请求完成整个过程。


五、生成默认代理类

理解了第四步的逻辑,生成默认代理类就很容易理解了,唯一不同点就是 client的实现类为 loadBalanceClient

注意:不管是哪种代理类,最终发起请求还是由 Feign.Default中的 execute方法完成,默认使用 HttpUrlConnection实现。


六、注入spring容器

总结:通过 spring refresh()方法,触发 FeignClientFactoryBean.getObject()方法获得了代理类,然后完成注入 spring容器的过程。该实现方式同 Dubbo的实现方式类似,有兴趣的可以自行研究噢。

更多阅读

史上最简单的 SpringCloud 教程汇总

SpringBoot教程汇总

Java面试题系列汇总

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年02月27日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • Feign架构图
  • 源码分析
    • 一、注册FeignClient配置类和FeignClient BeanDefinition
      • 先来隆重介绍一下 FeignClientFactoryBean,后面四步都基于此类。
        • 二、实例化Feign上下文对象FeignContext
          • 三、构造 FeignBuilder
            • 四、生成负载均衡代理类
              • 五、生成默认代理类
                • 六、注入spring容器
                  • 更多阅读
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档