十六.SpringCloud源码剖析-Feign源码分析

た 入场券 2022-12-20 12:36 329阅读 0赞

前言

Spring Cloud OpenFeign 对 Netflix Feign 进行了封装,我们通常都使用Spring Cloud OpenFeign作为服务的负载均衡,本文章主要是探讨一下OpenFeign的初始化流程,以及生成代理类注入到Spring的过程


一.Feign的基本使用

Feign是一个声明式的http客户端,使用Feign可以实现声明式REST调用,它的目的就是让Web Service调用更加简单。Feign整合了Ribbon和SpringMvc注解,这让Feign的客户端接口看起来就像一个Controller,Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理

1.导入依赖

这里使用的是spring-cloud-starter-openfeign:2.0.1.RELEASE 版本

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-openfeign</artifactId>
  4. </dependency>

2.开启Feign

通过@EnableFeignClients注解,他会去扫描指定包下的@FeignClient注解

  1. /** * 支付的启动类 * @EnableFeignClients :开启Feign支持 */
  2. @SpringBootApplication
  3. @EnableEurekaClient
  4. @EnableFeignClients(value="cn.itsource.springboot.feignclient")
  5. public class PayServerApplication1040
  6. {
  7. public static void main( String[] args )
  8. {
  9. SpringApplication.run(PayServerApplication1040.class);
  10. }
  11. }

3.Feign的客户端接口

  1. @FeignClient("user-server")
  2. public interface UserFeignClient {
  3. //订单服务来调用这个方法 http://localhost:1020/user/10
  4. // @GetMapping(value = "/user/{id}" )
  5. @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
  6. User getById(@PathVariable("id")Long id);
  7. }

通过主启动类上的@EnableFeignClients注解扫描@FeignClient标注的接口,这些接口会被代理,注入到Spring容器中

二.Feign的初始化

1.FeignAutoConfiguration Fegin的自动配置

同样是在SpringBoot启动的时候,执行自动装配流程,加载FeignAutoConfiguration自动配置类
在这里插入图片描述
看一下FeignAutoConfiguration源码

  1. @Configuration
  2. @ConditionalOnClass(Feign.class)
  3. //加载Fiegn的相关配置
  4. @EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class})
  5. public class FeignAutoConfiguration {
  6. //Feign的客户端配置,这里用的是@Autowired注入所有的FeignClientSpecification
  7. @Autowired(required = false)
  8. private List<FeignClientSpecification> configurations = new ArrayList<>();
  9. @Bean
  10. public HasFeatures feignFeature() {
  11. return HasFeatures.namedFeature("Feign", Feign.class);
  12. }
  13. //注册Feign的上下文对象,FeignContext会为每个Fiegn的客户端都创建一个Spring的ApplicationContext对象并从中提取所有的Bean
  14. @Bean
  15. public FeignContext feignContext() {
  16. FeignContext context = new FeignContext();
  17. context.setConfigurations(this.configurations);
  18. return context;
  19. }

FeignContext是Feign的上下文对象,FeignContext会为每个Fiegn的客户端都创建一个Spring的ApplicationContext上下文对象,并把相关的组件注册到容器中,并从中提取所有的Bean,这里的Fiegn的客户端指的是Fiegn的接口,会以@FeignClient(value=“服务名”) 注解指定的服务名作为客户端的名字。

思考一个问题,private List<FeignClientSpecification> configurations上面有个@Autowired,那FeignClientSpecification是在哪儿注册到Spring容器的呢???

FeignContext 继承了NamedContextFactory如下:

  1. public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
  2. public FeignContext() {
  3. super(FeignClientsConfiguration.class, "feign", "feign.client.name");
  4. }
  5. }

继承关系如下:
在这里插入图片描述

2.Feign的上下创建

在分析Ribbon的时候我们已经探讨过NamedContextFactory,在FeignClientAutoConfiguration注册FeignContext时,通过NamedContextFactory构造器初始化,然后调用NamedContextFactory.setConfigurations(this.configurations);方法加载配置

  1. public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
  2. implements DisposableBean, ApplicationContextAware {
  3. //上下文对象集合
  4. private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
  5. //配置列表
  6. private Map<String, C> configurations = new ConcurrentHashMap<>();
  7. ...省略...
  8. //初始化
  9. public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
  10. String propertyName) {
  11. //默认配置类,默认使用的是FeignClientsConfiguration
  12. this.defaultConfigType = defaultConfigType;
  13. //配置名,feign
  14. this.propertySourceName = propertySourceName;
  15. //feign.client.name
  16. this.propertyName = propertyName;
  17. }
  18. //设置配置,每个Feign的客户端接口都会对应一个配置
  19. public void setConfigurations(List<C> configurations) {
  20. for (C client : configurations) {
  21. this.configurations.put(client.getName(), client);
  22. }
  23. }
  24. //销毁工作,清除上下文对象
  25. public void destroy() {
  26. Collection<AnnotationConfigApplicationContext> values = this.contexts.values();
  27. for (AnnotationConfigApplicationContext context : values) {
  28. // This can fail, but it never throws an exception (you see stack traces
  29. // logged as WARN).
  30. context.close();
  31. }
  32. this.contexts.clear();
  33. }
  34. //根据名字获取上下文对象,如:传入服务名“user-server”获取上下文对象
  35. protected AnnotationConfigApplicationContext getContext(String name) {
  36. if (!this.contexts.containsKey(name)) {
  37. //如果容器集合中还没有当前客户端的上下文对象,就调用createContext方法创建客户端上下文对象
  38. //然后存储到contexts集合中
  39. synchronized (this.contexts) {
  40. if (!this.contexts.containsKey(name)) {
  41. this.contexts.put(name, createContext(name));
  42. }
  43. }
  44. }
  45. //根据客户端名字获取上下文对象
  46. return this.contexts.get(name);
  47. }
  48. //根据客户端名字,创建上下文
  49. protected AnnotationConfigApplicationContext createContext(String name) {
  50. //new一个 基于注解的ApplicationContext对象,用来装载Feign的客户端
  51. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  52. //如果 configurations 中包含了客户端的配置,把配置设置到创建的上下文中
  53. if (this.configurations.containsKey(name)) {
  54. for (Class<?> configuration : this.configurations.get(name)
  55. .getConfiguration()) {
  56. context.register(configuration);
  57. }
  58. }
  59. //注册默认的配置“default.”到上下文中
  60. for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
  61. if (entry.getKey().startsWith("default.")) {
  62. for (Class<?> configuration : entry.getValue().getConfiguration()) {
  63. context.register(configuration);
  64. }
  65. }
  66. }
  67. //注册了默认的配置类,defaultConfigType就是FeignClientConfiguration
  68. context.register(PropertyPlaceholderAutoConfiguration.class,
  69. this.defaultConfigType);
  70. context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
  71. this.propertySourceName,
  72. Collections.<String, Object> singletonMap(this.propertyName, name)));
  73. if (this.parent != null) {
  74. // Uses Environment from parent as well as beans
  75. context.setParent(this.parent);
  76. }
  77. context.setDisplayName(generateDisplayName(name));
  78. //刷新容器
  79. context.refresh();
  80. return context;
  81. }
  82. protected String generateDisplayName(String name) {
  83. return this.getClass().getSimpleName() + "-" + name;
  84. }
  85. //根据名字和类型从上下文中获取实例
  86. public <T> T getInstance(String name, Class<T> type) {
  87. //获取上下文
  88. AnnotationConfigApplicationContext context = getContext(name);
  89. if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
  90. type).length > 0) {
  91. //从上下文中获取实例
  92. return context.getBean(type);
  93. }
  94. return null;
  95. }

NamedContextFactory主要提供了

  • getContext(String name) : 根据客户端名字从 Map contexts 中获取上下文对象的方法,如果contexts中没有上下文会调用createContext(name) 创建上下文,然后放入contexts中再返回
  • createContext(String name) :根据客户端名字创建上下文对象,方法中分别注册了RibbonClient客户端配置类,default开头的所有默认配置,以及FeignClientsConfiguration默认配置,然后刷新容器,返回上下文对象
  • getInstance(String name, Class type) :根据名字和类型返回实例,方法先根据名字得到上下文对象然后从上下文对象中获取Bean

三.Feign的客户端注册

我们接着来看下Feign是如何对客户端接口进行注册到Spring容器中的,从@EnableFeignClients标签入手

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

该注解导入了FeignClientsRegistrar,见名知意,它对Feign的客户端的注册器

  1. class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
  2. ResourceLoaderAware, EnvironmentAware {
  3. // patterned after Spring Integration IntegrationComponentScanRegistrar
  4. // and RibbonClientsConfigurationRegistgrar
  5. private ResourceLoader resourceLoader;
  6. private Environment environment;
  7. public FeignClientsRegistrar() {
  8. }
  9. @Override
  10. public void setResourceLoader(ResourceLoader resourceLoader) {
  11. this.resourceLoader = resourceLoader;
  12. }
  13. //registerBeanDefinitions方法是ImportBeanDefinitionRegistrar接口中的用来注册Bean到Spring容器的方法
  14. @Override
  15. public void registerBeanDefinitions(AnnotationMetadata metadata,
  16. BeanDefinitionRegistry registry) {
  17. //注册默认的配置
  18. registerDefaultConfiguration(metadata, registry);
  19. //注册Feign的客户端
  20. registerFeignClients(metadata, registry);
  21. }

registerBeanDefinitions调用两个方法,

  • registerDefaultConfiguration:注册默认配置,
  • registerFeignClients注册Feign的客户端

1.Feign的默认配置注册

我们先看registerDefaultConfiguration方法

  1. private void registerDefaultConfiguration(AnnotationMetadata metadata,
  2. BeanDefinitionRegistry registry) {
  3. //得到@EnableFeignClients注解的原始信息
  4. Map<String, Object> defaultAttrs = metadata
  5. .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
  6. //是否配置了@EnableFeignClients(defaultConfiguration=)默认配置
  7. //使用的类型是FeignClientsConfiguration
  8. if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
  9. String name;
  10. //处理类型,以default开头
  11. if (metadata.hasEnclosingClass()) {
  12. name = "default." + metadata.getEnclosingClassName();
  13. }
  14. else {
  15. name = "default." + metadata.getClassName();
  16. }
  17. //注册默认配置,name 默认以 default 开头,后续会根据名称选择配置
  18. registerClientConfiguration(registry, name,
  19. defaultAttrs.get("defaultConfiguration"));
  20. }
  21. }

上面这个方法在为@EnableFeignClients(defaultConfiguration=)默认配置类做注册,一般可以不配置该属性

  1. private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
  2. Object configuration) {
  3. //创建一个BeanDefinitionBuilder
  4. BeanDefinitionBuilder builder = BeanDefinitionBuilder
  5. .genericBeanDefinition(FeignClientSpecification.class);
  6. //添加参数,把配置类交给builder
  7. builder.addConstructorArgValue(name);
  8. builder.addConstructorArgValue(configuration);
  9. //注册了Bean到Spring容器
  10. registry.registerBeanDefinition(
  11. name + "." + FeignClientSpecification.class.getSimpleName(),
  12. builder.getBeanDefinition());
  13. }

这里是把配置封装成FeignClientSpecification类型,然后通过BeanDefinitionRegistry注册到Spring容器中,该配置类包含了FeignClient的重试,超时,日志等配置,如服务定义配置会使用默认的配置。

还记得在FeignAutoConfiguration配置类中的private List<FeignClientSpecification> configurations吗?这是不是前后呼应起来了呢?

2.Feign的客户端注册

我们在看一下 registerFeignClients 方法

  1. public void registerFeignClients(AnnotationMetadata metadata,
  2. BeanDefinitionRegistry registry) {
  3. //ClassPathScanningCandidateComponentProvider是用来扫Feign描客户端接口的
  4. ClassPathScanningCandidateComponentProvider scanner = getScanner();
  5. //resourceLoader是用来加载类资源的
  6. scanner.setResourceLoader(this.resourceLoader);
  7. Set<String> basePackages;
  8. //获取@EnableFeignClients注解元数据
  9. Map<String, Object> attrs = metadata
  10. .getAnnotationAttributes(EnableFeignClients.class.getName());
  11. //类型过滤器,过滤FeignClient
  12. AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
  13. FeignClient.class);
  14. //检查是否配置clients属性
  15. final Class<?>[] clients = attrs == null ? null
  16. : (Class<?>[]) attrs.get("clients");
  17. if (clients == null || clients.length == 0) {
  18. //默认走这里
  19. //如果没有配置@EnableFeignClient(clients={})属性,就获取basePackages属性
  20. //给扫描添加includeFilters过滤器,目的是为了扫描@FeignClient注解
  21. scanner.addIncludeFilter(annotationTypeFilter);
  22. basePackages = getBasePackages(metadata);
  23. }
  24. else {
  25. //如果配置了clients就把 clients的包添加到扫描器中
  26. final Set<String> clientClasses = new HashSet<>();
  27. basePackages = new HashSet<>();
  28. for (Class<?> clazz : clients) {
  29. basePackages.add(ClassUtils.getPackageName(clazz));
  30. clientClasses.add(clazz.getCanonicalName());
  31. }
  32. AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
  33. @Override
  34. protected boolean match(ClassMetadata metadata) {
  35. String cleaned = metadata.getClassName().replaceAll("\\$", ".");
  36. return clientClasses.contains(cleaned);
  37. }
  38. };
  39. scanner.addIncludeFilter(
  40. new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
  41. }
  42. for (String basePackage : basePackages) {
  43. //把basePackage下的注解了@FeignClient的接口封装成BeanDefinition
  44. Set<BeanDefinition> candidateComponents = scanner
  45. .findCandidateComponents(basePackage);
  46. for (BeanDefinition candidateComponent : candidateComponents) {
  47. if (candidateComponent instanceof AnnotatedBeanDefinition) {
  48. // verify annotated class is an interface
  49. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
  50. //获取每个接口的注解元数据
  51. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
  52. Assert.isTrue(annotationMetadata.isInterface(),
  53. "@FeignClient can only be specified on an interface");
  54. //获取@FeignClient注解上的属性
  55. Map<String, Object> attributes = annotationMetadata
  56. .getAnnotationAttributes(
  57. FeignClient.class.getCanonicalName());
  58. //获取客户端名字
  59. String name = getClientName(attributes);
  60. //获取@FeignClient注解的configuration配置,对配置进行注册
  61. registerClientConfiguration(registry, name,
  62. attributes.get("configuration"));
  63. //注册客户端接口feignClient
  64. registerFeignClient(registry, annotationMetadata, attributes);
  65. }
  66. }
  67. }
  68. }

上面代码主要扫描了注解了@FeignClient的接口,会扫描到很多的接口,然后做2个事情

  • 将其配置类通过registerClientConfiguration注册到容器中,这个方法在刚才已经分析过了,这里是第二次调用
  • 通过registerFeignClient注册FeignClient接口到容器,内部会生成接口的代理类

继续跟进registerFeignClient

  1. private void registerFeignClient(BeanDefinitionRegistry registry,
  2. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
  3. //获取到组合的Feign接口类名
  4. String className = annotationMetadata.getClassName();
  5. //FeignClientFactoryBean是用用来创建FeignClient接口的,
  6. //这里要对Feign接口进行代理,使用的是FeignClientFactoryBean
  7. BeanDefinitionBuilder definition = BeanDefinitionBuilder
  8. .genericBeanDefinition(FeignClientFactoryBean.class);
  9. //校验@FeignClient相关属性
  10. validate(attributes);
  11. //封装相关的属性到definition
  12. definition.addPropertyValue("url", getUrl(attributes));
  13. definition.addPropertyValue("path", getPath(attributes));
  14. String name = getName(attributes);
  15. definition.addPropertyValue("name", name);
  16. definition.addPropertyValue("type", className);
  17. definition.addPropertyValue("decode404", attributes.get("decode404"));
  18. definition.addPropertyValue("fallback", attributes.get("fallback"));
  19. definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
  20. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  21. String alias = name + "FeignClient";
  22. //得到BeanDefinition对象,它是对Feign客户端的Bean的定义对象,基于FeignClientFactoryBean
  23. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  24. boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
  25. beanDefinition.setPrimary(primary);
  26. String qualifier = getQualifier(attributes);
  27. if (StringUtils.hasText(qualifier)) {
  28. alias = qualifier;
  29. }
  30. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
  31. new String[] { alias });
  32. //将BeanDefinition加入到spring容器
  33. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  34. }

从上面方法可以看出,Feign的客户端是通过Spring的工厂生成代理类,Feign接口被封装成BeanDefinition,类型是FeignClientFactoryBean,也就是说Feign把所有的接口都包装成FeignClientFactoryBean

思考一个问题,这里虽然对Feign接口进行了描述,封装成FeignClientFactoryBean,那么接口实例到底在什么时候创建呢???

3.FeignClientFactoryBean 工厂

这个工厂是用来生成Feign接口的代理类的,我们看一下他是如何做的

  1. class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
  2. ApplicationContextAware {
  3. /*********************************** * WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition. ***********************************/
  4. //Feign接口类型
  5. private Class<?> type;
  6. //Fiegn客户端名字/请求的服务名
  7. private String name;
  8. //请求的url
  9. private String url;
  10. private String path;
  11. private boolean decode404;
  12. private ApplicationContext applicationContext;
  13. private Class<?> fallback = void.class;
  14. //降级工厂
  15. private Class<?> fallbackFactory = void.class;
  16. ....省略....
  17. @Override
  18. public Object getObject() throws Exception {
  19. //获取Feign的上下文,在Feign的初始化一节有说道
  20. FeignContext context = applicationContext.getBean(FeignContext.class);
  21. //调用feign方法得到Builder构造器,用来生成feign
  22. //开启Hystrix使用的是HystrixFeign.Builder
  23. //否则使用的是feign.Feign.Builder
  24. Feign.Builder builder = feign(context);
  25. //如果没有url,即没有指定@FeignClient(url=""),就根据服务名拼接url
  26. if (!StringUtils.hasText(this.url)) {
  27. String url;
  28. //拼接请求的url如:http://user-server
  29. if (!this.name.startsWith("http")) {
  30. url = "http://" + this.name;
  31. }
  32. else {
  33. url = this.name;
  34. }
  35. url += cleanPath();
  36. //生成负载均衡代理类
  37. return loadBalance(builder, context, new HardCodedTarget<>(this.type,
  38. this.name, url));
  39. }
  40. //如果配置了@FeignClient(url=""),生成默认代理
  41. if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
  42. this.url = "http://" + this.url;
  43. }
  44. String url = this.url + cleanPath();
  45. Client client = getOptional(context, Client.class);
  46. if (client != null) {
  47. if (client instanceof LoadBalancerFeignClient) {
  48. // not load balancing because we have a url,
  49. // but ribbon is on the classpath, so unwrap
  50. client = ((LoadBalancerFeignClient)client).getDelegate();
  51. }
  52. builder.client(client);
  53. }
  54. Targeter targeter = get(context, Targeter.class);
  55. return targeter.target(this, builder, context, new HardCodedTarget<>(
  56. this.type, this.name, url));
  57. }

FeignClientFactoryBean实现了FactoryBean,getObject方法即是用来创建FeignClient实例的

  • 该方法授权通过ApplicationContext得到FeignContext上下文
  • 然后在从FeignContext上下文中得到Feign.Builder,它是用来构建Feign客户端代理类的
  • 然后根据Feign接口 的URL(要么通过@FeignClient(url=“”)指定,如果没指定,就根据@FeignClient(value=“服务名”)拼接) 生成负载均衡代理类

    为什么这里要判断URL呢?如果我们不指定URL会默认走Ribbon的负载均衡

    1. @FeignClient(value="user-server")

    如果指定了URL会走默认的负载均衡,相当于不走负载均衡

    1. @FeignClient(value="user-server",url="localhost:8989")

Feign.Builder

这里的Feign.Builder 是从FeignContext中得到的,我们看一下它的源码

  1. public static class Builder {
  2. //请求拦截器
  3. private final List<RequestInterceptor> requestInterceptors = new ArrayList();
  4. ...省略...
  5. public Builder() {
  6. //Feign的日志打印级别,默认不打印
  7. this.logLevel = Level.NONE;
  8. this.contract = new Default();
  9. this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
  10. //重试策略
  11. this.retryer = new feign.Retryer.Default();
  12. this.logger = new NoOpLogger();
  13. //编码器
  14. this.encoder = new feign.codec.Encoder.Default();
  15. //解码器
  16. this.decoder = new feign.codec.Decoder.Default();
  17. //错误解码器
  18. this.errorDecoder = new feign.codec.ErrorDecoder.Default();
  19. this.options = new Options();
  20. //方法调用工厂
  21. this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
  22. }
  23. //设置日志级别
  24. public Feign.Builder logLevel(Level logLevel) {
  25. this.logLevel = logLevel;
  26. return this;
  27. }
  28. public Feign.Builder contract(Contract contract) {
  29. this.contract = contract;
  30. return this;
  31. }
  32. public Feign.Builder client(Client client) {
  33. this.client = client;
  34. return this;
  35. }
  36. public Feign.Builder retryer(Retryer retryer) {
  37. this.retryer = retryer;
  38. return this;
  39. }
  40. public Feign.Builder logger(Logger logger) {
  41. this.logger = logger;
  42. return this;
  43. }
  44. public Feign.Builder encoder(Encoder encoder) {
  45. this.encoder = encoder;
  46. return this;
  47. }
  48. public Feign.Builder decoder(Decoder decoder) {
  49. this.decoder = decoder;
  50. return this;
  51. }
  52. public Feign.Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) {
  53. this.decoder = new Feign.ResponseMappingDecoder(mapper, decoder);
  54. return this;
  55. }
  56. public Feign.Builder decode404() {
  57. this.decode404 = true;
  58. return this;
  59. }
  60. public Feign.Builder errorDecoder(ErrorDecoder errorDecoder) {
  61. this.errorDecoder = errorDecoder;
  62. return this;
  63. }
  64. public Feign.Builder options(Options options) {
  65. this.options = options;
  66. return this;
  67. }
  68. public Feign.Builder requestInterceptor(RequestInterceptor requestInterceptor) {
  69. this.requestInterceptors.add(requestInterceptor);
  70. return this;
  71. }
  72. //添加Feign的拦截器
  73. public Feign.Builder requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) {
  74. this.requestInterceptors.clear();
  75. Iterator var2 = requestInterceptors.iterator();
  76. while(var2.hasNext()) {
  77. RequestInterceptor requestInterceptor = (RequestInterceptor)var2.next();
  78. this.requestInterceptors.add(requestInterceptor);
  79. }
  80. return this;
  81. }
  82. public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
  83. this.invocationHandlerFactory = invocationHandlerFactory;
  84. return this;
  85. }
  86. public <T> T target(Class<T> apiType, String url) {
  87. return this.target(new HardCodedTarget(apiType, url));
  88. }
  89. //这个方法挺重要的,它是用来构建FeignClient的代理类的
  90. public <T> T target(Target<T> target) {
  91. return this.build().newInstance(target);
  92. }
  93. //注意:这里使用的是ReflectiveFeign作为Feign的默认实现
  94. //创建Feign接口的代理就是ReflectiveFeign搞定的
  95. public Feign build() {
  96. Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404);
  97. ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.errorDecoder, synchronousMethodHandlerFactory);
  98. return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory);
  99. }
  100. }

Feign.Builder中封装了Feign的日志注解,编码器,解码器,拦截器等等,这些东西是在FeignClientsConfiguration中被创建的

FeignClientsConfiguration

  1. @Configuration
  2. public class FeignClientsConfiguration {
  3. //Feign的消息转换器
  4. @Autowired
  5. private ObjectFactory<HttpMessageConverters> messageConverters;
  6. @Autowired(required = false)
  7. private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
  8. @Autowired(required = false)
  9. private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
  10. @Autowired(required = false)
  11. private Logger logger;
  12. //解码器
  13. @Bean
  14. @ConditionalOnMissingBean
  15. public Decoder feignDecoder() {
  16. return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
  17. }
  18. //编码器
  19. @Bean
  20. @ConditionalOnMissingBean
  21. public Encoder feignEncoder() {
  22. return new SpringEncoder(this.messageConverters);
  23. }
  24. @Bean
  25. @ConditionalOnMissingBean
  26. public Contract feignContract(ConversionService feignConversionService) {
  27. return new SpringMvcContract(this.parameterProcessors, feignConversionService);
  28. }
  29. @Bean
  30. public FormattingConversionService feignConversionService() {
  31. FormattingConversionService conversionService = new DefaultFormattingConversionService();
  32. for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
  33. feignFormatterRegistrar.registerFormatters(conversionService);
  34. }
  35. return conversionService;
  36. }
  37. //如果开启了Hystrix,那么使用的是HystrixFeign.builder();
  38. //而不是Feign.Builder
  39. @Configuration
  40. @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
  41. protected static class HystrixFeignConfiguration {
  42. @Bean
  43. @Scope("prototype")
  44. @ConditionalOnMissingBean
  45. @ConditionalOnProperty(name = "feign.hystrix.enabled")
  46. public Feign.Builder feignHystrixBuilder() {
  47. return HystrixFeign.builder();
  48. }
  49. }
  50. //重试策略
  51. @Bean
  52. @ConditionalOnMissingBean
  53. public Retryer feignRetryer() {
  54. return Retryer.NEVER_RETRY;
  55. }
  56. //注册Feign.Builder
  57. @Bean
  58. @Scope("prototype")
  59. @ConditionalOnMissingBean
  60. public Feign.Builder feignBuilder(Retryer retryer) {
  61. return Feign.builder().retryer(retryer);
  62. }
  63. //日志工厂
  64. @Bean
  65. @ConditionalOnMissingBean(FeignLoggerFactory.class)
  66. public FeignLoggerFactory feignLoggerFactory() {
  67. return new DefaultFeignLoggerFactory(logger);
  68. }
  69. }

loadBalance方法

回到getObject方法看一下loadBalance方法是如何生成负载均衡代理类的

  1. protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
  2. HardCodedTarget<T> target) {
  3. //从上下文对象FeignContext中获取Client,默认使用的是LoadBalancerFeignClient
  4. Client client = getOptional(context, Client.class);
  5. if (client != null) {
  6. //client设置给builder
  7. builder.client(client);
  8. Targeter targeter = get(context, Targeter.class);
  9. //targeter的target方法调用了Feign的target方法
  10. //Feign的默认实现是ReflectiveFeign类,最终调用了ReflectiveFeign.newInstance方法创建实例
  11. return targeter.target(this, builder, context, target);
  12. }
  13. throw new IllegalStateException(
  14. "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
  15. }

这里会从FeignContext上下文中获取到LoadBalancerFeignClient负载客户端设置给Feign.Builder ,然后从FeignContext上下文得到Targeter,通过Targeter.target方法创建实例,底层通过Feign.newInstance方法完成实例创建,默认实现是ReflectiveFeign完成创建

4.ReflectiveFeign创建Feign代理

ReflectiveFeign通过Proxy.newProxyInstance为Feign接口生成代理

  1. public class ReflectiveFeign extends Feign {
  2. ...省略...
  3. //target是HardCodedTarget,封装了客户端的type,name,url
  4. public <T> T newInstance(Target<T> target) {
  5. //得到Feign接口中的所有的方法MethodHandler
  6. Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
  7. //把 Map<String, MethodHandler>变成Map<Method, MethodHandler>
  8. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
  9. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
  10. Method[] var5 = target.type().getMethods();
  11. int var6 = var5.length;
  12. for(int var7 = 0; var7 < var6; ++var7) {
  13. Method method = var5[var7];
  14. if (method.getDeclaringClass() != Object.class) {
  15. if (Util.isDefault(method)) {
  16. //这里在添加默认的方法
  17. DefaultMethodHandler handler = new DefaultMethodHandler(method);
  18. defaultMethodHandlers.add(handler);
  19. methodToHandler.put(method, handler);
  20. } else {
  21. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  22. }
  23. }
  24. }
  25. InvocationHandler handler = this.factory.create(target, methodToHandler);
  26. //生成代理
  27. T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{ target.type()}, handler);
  28. Iterator var12 = defaultMethodHandlers.iterator();
  29. while(var12.hasNext()) {
  30. //代理类绑定默认的方法
  31. DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
  32. defaultMethodHandler.bindTo(proxy);
  33. }
  34. //返回代理
  35. return proxy;
  36. }

到这里,Feign的客户端的注册流程就结束了,那注册是在什么时候发生的呢?是在Spring启动过程中会调用refresh()方法,在该方法中会触发FeignClientFactoryBean.getObject()得到实例注册到Spring容器中

四.总结

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,329人围观)

还没有评论,来说两句吧...

相关阅读