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 请求
评论