OpenFeign 服务注册和调用原理
Feign 注册到容器
和 springboot 自动配置原理类似,在 springboot 启动时会扫描到 EnableFeignClients 注解,这个注解导入了一个 FeignClientsRegistrar 类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {}
FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar,所以会通过 registerBeanDefinitions 方法注入一批 BeanDefinition
// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注册配置类(上一篇文章说过的 EnableFeignClients.defaultConfiguration 属性)
registerDefaultConfiguration(metadata, registry);
// 注册 feign 客户端
registerFeignClients(metadata, registry);
}
// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients
// registerFeignClients,参数应该不陌生了,一个是元注解信息,一个是 ImportBeanDefinitionRegistrar 用来注册 bean 的
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// 通过元注解获取到注解属性(就是 EnableFeignClients 的配置项)
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
// 先看有没有配 clients
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
// 扫描使用了 FeignClient 注解的类
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
// clients 指定的类
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
...
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
实际注册 bean 的方法 registerFeignClient,里面根据配置信息构建了一个 BeanDefinition,构建的过程还是比较简单就是设置 beanName、type、lazy 等基础信息和 EnableFeignClients 配置的 fallback 等
// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
// 是否延迟加载,读取的配置项,true:使用 feign 时才创建;false(默认):项目启动时创建
if (String.valueOf(false).equals(this.environment.getProperty("spring.cloud.openfeign.lazy-attributes-resolution", String.valueOf(false)))) {
this.eagerlyRegisterFeignClientBeanDefinition(className, attributes, registry);
} else {
this.lazilyRegisterFeignClientBeanDefinition(className, attributes, registry);
}
}
// org.springframework.cloud.openfeign.FeignClientsRegistrar#eagerlyRegisterFeignClientBeanDefinition
private void eagerlyRegisterFeignClientBeanDefinition(String className, Map<String, Object> attributes, BeanDefinitionRegistry registry) {
this.validate(attributes);
// 构建一个 BeanDefinitionBuilder(genericBeanDefinition 方法把 bealClass 设置为 FeignClientFactoryBean)
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
definition.addPropertyValue("url", this.getUrl((ConfigurableBeanFactory)null, attributes));
definition.addPropertyValue("path", this.getPath((ConfigurableBeanFactory)null, attributes));
String name = this.getName(attributes);
definition.addPropertyValue("name", name);
String contextId = this.getContextId((ConfigurableBeanFactory)null, attributes);
definition.addPropertyValue("contextId", contextId);
// 省略了 BeanDefinitionBuilder 的赋值操作
...
// 根据 beanDefinition 再构建一个 BeanDefinitionHolder
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
// 关联 BeanDefinitionHolder 和 registry(registry 持有 BeanDefinitionHolder,后续通过 registry 创建对象)
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
// 方法调用了两次,但是参数不一样,一个是创建 Feign 的配置项(Request.Options,超时配置),一个是 Feign
this.registerRefreshableBeanDefinition(registry, contextId, Request.Options.class, OptionsFactoryBean.class);
this.registerRefreshableBeanDefinition(registry, contextId, RefreshableUrl.class, RefreshableUrlFactoryBean.class);
}
// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerRefreshableBeanDefinition
private void registerRefreshableBeanDefinition(BeanDefinitionRegistry registry, String contextId, Class<?> beanType,
Class<?> factoryBeanType) {
if (isClientRefreshEnabled()) {
String beanName = beanType.getCanonicalName() + "-" + contextId;
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(factoryBeanType);
definitionBuilder.setScope("refresh");
definitionBuilder.addPropertyValue("contextId", contextId);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definitionBuilder.getBeanDefinition(),
beanName);
// 反射创建对象(前面说了 beanClass 是 FactoryBean)
definitionHolder = ScopedProxyUtils.createScopedProxy(definitionHolder, registry, true);
// 注册 beanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
}
FactoryBean 这个在前面的文章有说过,这是 spring 的知识,工厂方式构建 bean 的方式:1,使用静态方法;2:使用实例方法;3:使用 FactoryBean。如果是 FactoryBean,最终的对象是 getObject 方法返回的对象作为 bean,最终的代理对象怎么生成的就不贴源码了,代码量也不多,看下最终的代理对象长啥样吧

Feign 调用流程
就是把容器中 Feign 的代理对象取出来,然后反射执行方法的过程,先对比下刚才放到单例池的代理对象和注入的对象

先判断是否是 Object 的方法,然后执行代理对象的方法,最后发送一个 http 请求,调用链如下

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具