EnableFeignClient source code exploration
Let’s get started without further ado!
Click into @EnableFeignClient
this annotation:
The core one is this FeignClientRegistrar, we click into it:
We found that it implements the three interfaces ImportBeanDefinitionRegistrar, ResourceLoaderAware, and EnvironmentAware. I won’t go into too much detail about the Aware interface at the end. Students who are familiar with Spring should all know it. Let’s take a look at this ImportBeanDefinitionRegistrar.
What is this used for? Without further ado, let’s go to the breakpoint on ImportBeanDefinitionRegistrar. The fastest way to learn a framework is to debug it. If you debug it from beginning to end, you will understand everything.
After setting a breakpoint and starting Debug, you can see that it has reached this line. The first one is registerDefaultConfiguration
. As the name suggests, it is the registered default configuration. Let’s follow up to see Let’s see what the tricks are:
You can see that it has obtained all the attributes in the annotation through getAnnotationAttributes
, but there is no assignment. followed by: @EnableFeignClients
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
//判断有没有父类
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
//如果没有,就获取该类的全限定类名
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
Because we have not inherited other classes, the name we get here is:
Finally, we called the registerClientConfiguration
method. Let’s click in and take a look. What does this method do:
We can see that in this method, it passes in a BeanDefinitionRegistry, and the Bean information used in the context is constructed through this registry.
This Builder first constructs the FeignClientSpecification, then passes the class name of the main function and the configuration items, and then registers the Bean from the BeanDefinitionRegistry.
Then we return to the methodregisterBeanDefinitions
:
We have just finished reading the first method, which is to register the default configuration. Next is this registerFeignClients
The method is closely related to Feign.
This FeignClients is these things:
Let’s see how it is registered:
First, the first step, it is Obtained a scanner, which is used for scanning packages. A resourceLoader is set up below, which is unremarkable. Continue reading:
EnableFeignClients is obtained again here, and declared below A Filter, this Filter will filter some classes annotated by FeignClient.
Next:
Here it is judged whether the attrs is null. If it is not null, the declared clients are obtained, because we did not specify the client in the annotation. Which clients are loaded, so this attrs is empty, and then:
If you do not specify which clients to load, you will reach this step. The Filter just used here is to load all the FeignClient annotation statements. class, and a basePackages is obtained from matedata. Let's follow up to see how it is obtained:
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
//从EnableFeignClients里获取属性
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
Set<String> basePackages = new HashSet<>();
//下面这几个for循环都是获取属性值
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
//如果都没有声明
if (basePackages.isEmpty()) {
basePackages.add(
//拿当前声明的main函数的那个类来扫描
ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
Since we did not declare it here, it took the alternative and obtained the package of the main function declared by EnableFeignClient.
After we obtained the basePackage, we came here:
Here it will cycle the basePackages, and then use the scanner to scan the basePackage. We saw before This scanner adds an AnnotationTypeFilter, then it will find all the Class or Interface loaded with the corresponding Annotation in this package path. Let's take a step down and take a look:
He gave us It finds this IService, and then it loops this candidate:
First, because our IService is declared with the FeignClient annotation, it must be of the type AnnotatedBeanDefinition, so we enter the if:
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
//转换成AnnotatedBeanDefinition
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
//获取Metadata,也就是获取主数据
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
//这里要求annotationMetadata必须是一个接口,否则就报错
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
//然后从annotationMetadata拿到FeignClient属性
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//获取clientName
String name = getClientName(attributes);
//注册配置信息
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注册FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
Let’s take a look at this step, how it obtains the ClientName:
Follow up:
First, it checks whether contextId is declared in your annotation , if not, the value will be obtained:
This value is the eureka-client declared in our annotation.
If the value is not obtained, get it from the name. If the value is not found, get it from the serviceId. If you cannot get anything, then an error will be reported:
Also That is to say, when we declare the FeignClient annotation, you must specify a serviceId, or name, value, etc. for it.
In short, you need to let it know the object it wants to proxy for this interface and which microservice it needs to forward the request to. Feign cannot work without this attribute.
Let’s look at the next method:
Follow up:
This step is to register the Client’s configuration information. In fact, this wave is in the context Construct BeanDefinitioin.
It is very simple, just pass in the name and configuration items, and finally register the Bean in the registry.
We return:
The last step is to register FeignClient, follow up to see:
The front is simple, first get the class declared by Feign Fully qualified class name, and then declare a BeanDefinitionBuilder, and then verify the attributes here, let's follow up and take a look:
Here are some logics involving Hystrix downgrade fault tolerance, let's not worry about it for now. .
Then return:
After setting a lot of attributes here, an injection type is set here, which is injected based on the type.
We go further down and see here:
Here a BeanDefinitionHolder is constructed and an alias is set, and then it is associated with BeanDefinitionRegistry through registerBeanDefinition
Got up and completed the registration.
Here our entire processregisterBeanDefinitions
is over.