Feign core source code analysis

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.

image

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:

image

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.

image

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)

image

After that, call the registerFeignClient method:

image

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.

image

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:

image

Call the loadBalance method:

image

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:

image

Then Feign’s target method is called:

image

Finally, the newInstance method in the ReflectiveFeign class is called. The Map named nameToHandler stores the methods defined in the FeignClient interface:

image

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:

image

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:

image

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:

image

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:

image

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:

image

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.

image

Enter the execute method of LoadBalancerFeignClient, where a RibbonRequest request is constructed:

image

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.

image

Call the executeWithLoadBalancer method of AbstractLoadBalancerAwareClient:

image

Enter its submit method:

image

Enter the selectServer method:

image

Enter the getServerFromLoadBalancer method of LoadBalancerContext:

image

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:

image

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

image

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~