欢迎访问Spring Cloud中国社区

《重新定义Spring Cloud实战》由Spring Cloud中国社区倾力打造,基于Spring Cloud的Finchley.RELEASE版本,本书内容宽度足够广、深度足够深,而且立足于生产实践,直接从生产实践出发,包含大量生产实践的配置。欢迎加微信Software_King进群答疑,国内谁在使用Spring Cloud?欢迎登记

Feign终结解析

admin · 1月前 · 277 ·

概述

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

我们分别来看一下上面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生成对应的BeanDefinition:

  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,则调用FeignClientFactoryBeanT 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完成的,所以上图中引入了条件注解:

    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. //各组件`client,retryer,decoder`进行增强,装饰器模式。
  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. }
  1. public <T> T getInstance(String name, Class<T> type) {
  2. //这里获取AnnotationConfigApplicationContext容器
  3. AnnotationConfigApplicationContext context = getContext(name);
  4. if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
  5. type).length > 0) {
  6. return context.getBean(type);
  7. }
  8. return null;
  9. }
  10. private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
  11. protected AnnotationConfigApplicationContext getContext(String name) {
  12. if (!this.contexts.containsKey(name)) {
  13. synchronized (this.contexts) {
  14. if (!this.contexts.containsKey(name)) {
  15. //这里创建容器createContext(name)
  16. this.contexts.put(name, createContext(name));
  17. }
  18. }
  19. }
  20. return this.contexts.get(name);
  21. }

重点来了,上述代码将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的实现方式类似,有兴趣的可以自行研究噢。

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