Thoroughly understand Feign - Exploring the underlying mechanism of EnableFeignClient

EnableFeignClient source code exploration

Let’s get started without further ado!
Click into @EnableFeignClient this annotation:
Insert image description here
The core one is this FeignClientRegistrar, we click into it:
Insert image description here

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.
Insert image description here
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.
Insert image description here
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:
Insert image description here

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: Insert image description here
Finally, we called the registerClientConfiguration method. Let’s click in and take a look. What does this method do:
Insert image description here
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:
Insert image description here
We have just finished reading the first method, which is to register the default configuration. Next is this registerFeignClientsThe method is closely related to Feign.
This FeignClients is these things:
Insert image description here
Let’s see how it is registered:
Insert image description here
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:
Insert image description here
EnableFeignClients is obtained again here, and declared below A Filter, this Filter will filter some classes annotated by FeignClient.
Next:
Insert image description here
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:
Insert image description here
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;
	}

Insert image description here
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:
Insert image description 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:
Insert image description here
He gave us It finds this IService, and then it loops this candidate:
Insert image description here
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:
Insert image description here
Follow up:
Insert image description here
First, it checks whether contextId is declared in your annotation , if not, the value will be obtained:
Insert image description here
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:
Insert image description here
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:
Insert image description here
Follow up:
Insert image description here
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:
Insert image description here
The last step is to register FeignClient, follow up to see:
Insert image description here
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:
Insert image description here
Here are some logics involving Hystrix downgrade fault tolerance, let's not worry about it for now. .
Then return:
Insert image description here
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:
Insert image description 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.

Guess you like

Origin blog.csdn.net/qq_45455361/article/details/121459795