0%

Spring Open Feign

这是一篇基于Spring openfeign,关于Spring的一些常用组件,类,编写方式的笔记与源码阅读。

Spring openfeign封装了openfeign,这里暂时不讨论关于openfeign的详细实现,主要考虑
Spring通过什么方法将openfeign注入的。

Spring使用openfeign通过注解@EnableFeignClients,EnableFeignClients使用Import注解
,包含一个ImportBeanDefinitionRegistrar的实现类FeignClientsRegistrar。这里出现了
第一个关键接口ImportBeanDefinitionRegistrar,可以使用Import导入配置。
我们关注它的实现方法registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
// 调用方法
registerFeignClients(metadata, registry);
}

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
// 这里,通过EnableFeignClients中的clients()方法直接创建了BeanDefinition(BeanDefinition
// 是非常重要的关于Bean的一个类,请阅读Spring源码)
for (Class<?> clazz : clients) {
// AnnotatedGenericBeanDefinition是BeanDefinition的实现类。
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}

for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition beanDefinition) {
// verify annotated class is an interface
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
// 看到这个断言,@FeignClient只能直接接口
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");

Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());

String name = getClientName(attributes);
String className = annotationMetadata.getClassName();
registerClientConfiguration(registry, name, className, attributes.get("configuration"));

registerFeignClient(registry, annotationMetadata, attributes);
}
}
}

可以看到在registerFeignClients尝试获取EnableFeignClients注解,这里,并没有正式加载Feign client,只是获取一个全局的
配置,扫描包路径等,比较特殊的方法是clients(),直接获取关于@FeignClient的类。

接下来是注册Bean到容器,FeignClientRegister提供了两种注册,分别是eagerlyRegisterFeignClientBeanDefinition(className, attributes, registry)
lazilyRegisterFeignClientBeanDefinition(className, attributes, registry),选择哪一种由
spring.cloud.openfeign.lazy-attributes-resolution决定,默认为false,使用eagerlyRegisterFeignClientBeanDefinition。
这里需要注意一点,不论哪一种方法,其实都是注册了一个FactoryBean的子类,并不是直接注册Feign Client。

最后,我们发现,Spring的openfeign依然是通过feignClientFactory完成。