Feign 的整体流程
作用
feign 的作用就是根据 RPC 远程调用的接口生成动态代理实例,然后根据 SpringMVC 中的方法上的注解生成方法处理器,最终生成 HTTP 请求,通过 feignClient 发送给服务端。
整体流程
feign 的整体流程:
FeignClientsRegistrar 的 registerFeignClient()方法
在启动类添加注解 @EnableFeignClients,这个注解中导入了 FeignClientsRegistrar 类,FeignClientsRegistrar 类的主要功能就是通过扫描 @FeignClient 注解创建 FeignClientFactoryBean 实例,然后注入到 Spring 容器中。
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) { String className = annotationMetadata.getClassName(); BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); this.validate(attributes); definition.addPropertyValue("url", this.getUrl(attributes)); definition.addPropertyValue("path", this.getPath(attributes)); String name = this.getName(attributes); definition.addPropertyValue("name", name); definition.addPropertyValue("type", className); definition.addPropertyValue("decode404", attributes.get("decode404")); definition.addPropertyValue("fallback", attributes.get("fallback")); definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory")); definition.setAutowireMode(2); String alias = name + "FeignClient"; AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); boolean primary = (Boolean)attributes.get("primary"); beanDefinition.setPrimary(primary); String qualifier = this.getQualifier(attributes); if (StringUtils.hasText(qualifier)) { alias = qualifier; }
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias}); BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); }
复制代码
getTarget()方法
然后代码中通过 @Resource 等注解使用的时候,会通过 FeignClientFactoryBean 的 getObject()方法来获取到动态代理对象。动态代理对象的生成是通过 Feign.Builder 的 target 方法中调用 build()方法生成 ReflectiveFeign 的实例,然后通过 newInstance 创建最终的 RPC 动态代理的实例。
<T> T getTarget() { FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class); Builder builder = this.feign(context); String url; if (!StringUtils.hasText(this.url)) { if (!this.name.startsWith("http")) { url = "http://" + this.name; } else { url = this.name; }
url = url + this.cleanPath(); return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, url)); } else { if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { this.url = "http://" + this.url; }
url = this.url + this.cleanPath(); Client client = (Client)this.getOptional(context, Client.class); if (client != null) { if (client instanceof LoadBalancerFeignClient) { client = ((LoadBalancerFeignClient)client).getDelegate(); }
builder.client(client); }
Targeter targeter = (Targeter)this.get(context, Targeter.class); return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url)); } }
复制代码
动态代理的创建
创建动态代理实例的时候,feign 会先创建一个调用处理器,然后每个方法创建方法处理器,方法处理器缓存在调用处理器的 dispatch 集合中。动态代理实例进行方法调用的时候,Feign 根据反射实例从调用处理器的 dispatch 集合中找到对应的方法处理器,然后进行 Http 请求。方法处理器生成 Request 请求,然后交给 feign 的客户端进行调用,真正使用的时候用 LoadBalancerFeignClient,因为它可以进行负载均衡,它还会委托具体的 client 完成 http 请求。
总结
这篇文章主要讲了 Feign 的整体流程,主要是通过 @EnableFeignClients 注解引入 FeignClientsRegistrar 类,类中扫描 @FeignClient 注解,注入 FeignClientFactoryBean 实例,通过 getObject()方法获取动态实例,Feign 的远程调用实际上就是通过动态代理来完成的,动态代理的创建是 feign 根据利用反射机制创建代理对象,然后找到对应的方法处理器发起 http 请求
评论