As a declarative HTTP service client, Feign can complete the call to the service provider interface by adding annotations to the interface, which greatly simplifies our work when calling services.
01
Initialization Phase
First, take a look at Feign’s opening annotation @EnableFeignClients:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients {<!-- --> String[] value() default {<!-- -->}; String[] basePackages() default {<!-- -->}; Class<?>[] basePackageClasses() default {<!-- -->}; Class<?>[] defaultConfiguration() default {<!-- -->}; Class<?>[] clients() default {<!-- -->}; }
@Import imports FeignClientsRegistrar, which implements the ImportBeanDefinitionRegistrar interface. In the registerBeanDefinitions method of this interface, spring exposes the BeanDefinitionRegistry register. If users need to manually create or modify a BeanDefinition, they can register the BeanDefinition to the BeanDefinitionRegistry. Then spring will help us instantiate the bean and place it in the container.
Among the two methods, the registerDefaultConfiguration method is mainly used to read configuration information. Let’s mainly look at the implementation of the registerFeignClients method:
Here we first define a scanner, read the properties of the @EnableFeignClients annotation, and configure the FeignClient annotation type filter for subsequent packet scanning operations.
Through scanning, the BeanDefinition of all classes marked by @FeignClient annotation under the path defined by basepackage is obtained.
Read the content of the @FeignClient annotation and store it in a Map. Since I only specified name in the annotation, only the values of name and value exist (value is specified as an alias of name through @AliasFor)
After that, call the registerFeignClient method:
Note that what is created here through BeanDefinitionBuilder is a factory bean of FeignClientFactoryBean type. Note that what is returned through its getObject is our FeignClient. Then fill in the properties of the FeignClient object through the BeanDefinitionBuilder and obtain the BeanDefinition.
The BeanDefinitionHolder here can be understood as the packaging class of BeanDefinition, which provides a method to obtain BeanDefinition based on beanName, which can be understood as adding an additional layer of encapsulation.
After completing the property filling, register the just instantiated BeanDefinitionHolder with BeanDefinitionRegistry through the registerBeanDefinition method provided by Spring. What is done here is to hand over the class information annotated by FeignClient to the factory bean proxy class, and register the definition of the proxy class into the Spring container.
At this point, the information about the interface proxy object to be created has been put into the registry, and then spring will be responsible for instantiating the bean when calling the refresh method. During the instantiation process, call the getObject method of FeignClientFactoryBean:
Call the loadBalance method:
The Client instance created here is a LoadBalancerFeignClient object. Client is a very important component. Take a look at the instance injected in the configuration class:
@Configuration class DefaultFeignLoadBalancedConfiguration {<!-- --> @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {<!-- --> return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory); } }
When the Client is not configured, a LoadBalancerFeignClient will be injected, in which a Client is injected into the delegate attribute.
D
e
f
a
u
l
t
Objects can be temporarily understood as agents, which will be discussed later.
F
e
i
g
n
send
R
e
q
u
e
s
t
request and receive
R
e
s
p
o
n
s
e
Responses are all with the help of
C
l
i
e
n
t
The Default object can be temporarily understood as a proxy. As we will talk about later, Feign uses Client to send Request requests and receive Response responses.
The Default object can be temporarily understood as a proxy. As we will talk about later, Feign sends Request requests and receives Response responses, all with the help of the ClientDefault object.
You can recall the RibbonLoadBalancerClient mentioned in Ribbon before. Ribbon calls its execute method after using the interceptor. So we can guess, what method is used to finally call the execute method of LoadBalancerFeignClient? We will confirm this issue later.
02
Create proxy object
Next, look at the loadBalance method above. First, the target method of HystrixTargeter is called:
Then Feign’s target method is called:
Finally, the newInstance method in the ReflectiveFeign class is called. The Map named nameToHandler stores the methods defined in the FeignClient interface:
It will be clear when you see the InvocationHandler and Proxy below. As we said at the beginning, the proxy object is created using JDK dynamic proxy. Create InvocationHandler and proxy object process:
The factory here is the object of InvocationHandlerFactory. Take a look at its create method, which is used to create FeignInvocationHandler instances to intercept the method. The interface of the proxy class and the methods that require proxy are passed in the constructor:
03
Interception method
Through JDK dynamic proxy, we know that in InvocationHandler, the invoke method performs method interception and logic enhancement. Then we use a test interface to see how the key invoke method works:
First, based on the method name, it is judged whether it is some method built into the Object class. If not, a dispatch operation is performed. This dispatch is the MethodHandler list generated during the initialization phase. Call the invoke method of the SynchronousMethodHandler class:
Use RequestTemplate to create an http request template. You can see that a request is created here:
GET /user/1 HTTP/1.1
Enter the executeAndDecode method. In this method, a Request request is first generated using the template just created, and the service name and interface name we called this time are spliced together:
Here, the request is handed over to the previously created LoadBalancerFeignClient and its execute method is executed. As mentioned at the beginning, the calling process is similar to Ribbon. The only difference that needs to be made is that Ribbon uses an interceptor to intercept requests, while Feign uses the invoke method of the dynamic proxy to intercept and forward the method.
Enter the execute method of LoadBalancerFeignClient, where a RibbonRequest request is constructed:
In the above uriWithoutHost, the service name in the url is removed. This is done because Feign actually only needs the interface string behind the service. As for how to select the service and load balancing, it is left to Ribbon.
Entering the RibbonRequest construction method, you can see that the Client implementation class used is the Client$Default object, which is the object stored by the delegate in LoadBalancerFeignClient in the configuration file mentioned earlier.
Call the executeWithLoadBalancer method of AbstractLoadBalancerAwareClient:
Enter its submit method:
Enter the selectServer method:
Enter the getServerFromLoadBalancer method of LoadBalancerContext:
Ribbon is combined here to complete load balancing and select servers based on the load balancing algorithm.
Then call the execute method of FeignLoadBalancer, and then call the execute method of Client$Default:
Call convertAndSend to create an HttpURLConnection connection. Finally, the remote call is made using HttpURLConnection, and the result is encapsulated in the convertResponse method. At this point, the calling process is completed.
At this point, we can understand why it is not accurate to say that Feign is a Web client. In fact, it does not complete any request processing operations, it is just a pseudo client, and ultimately calls other components to complete the request sending and receiving.
Finally, let’s summarize Feign’s implementation process:
1. Use JDK dynamic proxy to create a proxy object for the interface
2. When executing the interface method, call the invoker method of the proxy object
3. Read the annotations of FeignClient to get the interface of the remote service to be called.
4. Obtain a service provider to be called through Ribbon load balancing
5. Use HttpURLConnection to initiate a request and get a response
Scan the QR code to follow the official account
Interesting, in-depth, direct
Talk to you about java
Reply “Interview” in the background of the official account and receive interview materials from major manufacturers.
If you find it useful, please click and take a look~