How does OpenFeign achieve "fetch from space"?

Source: https://blog.csdn.net/zgz15515397650/article/details/125415419

The predecessor of the OpenFeign component is the Netflix Feign project, which was originally developed by Netflix as part of the Netflix OSS project. Later, the Feign project was contributed to the open source organization, so we have the Spring Cloud OpenFeign components we use today.

OpenFeign provides a declarative remote call interface, which can greatly simplify the programming experience of remote calls. Before understanding the principle of OpenFeign, let's experience the ultimate effect of OpenFeign. I used a small case of Hello World to show you what the code style of the remote service call initiated by OpenFeign looks like.

String response = helloWorldService.hello("Vincent Y.");

You may ask, isn't this just a native method call? That's right! It is very simple to use OpenFeign components to implement remote calls, just like we use local methods, as long as one line of code can achieve what WebClient components do with several lines of code. Moreover, this code does not contain any business-independent information, which perfectly realizes the separation of responsibilities between calling logic and business logic.

So, how does the OpenFeign component implement remote calling at the bottom? Next, I will take you to understand the workflow behind the OpenFeign components.

OpenFeign uses a "dynamic proxy" technology to encapsulate the process of remote service calls. The helloWorldService we saw in the above example is actually a special interface, which is declared by the FeignClient annotation in the OpenFeign component. Interface The code in is as follows.

@FeignClient(value = "hello-world-serv") 
public interface HelloWorldService { 
    @PostMapping("/sayHello") 
    String hello(String guestName); 
}

At this point, you must have suddenly realized that the information of the remote service call is written in the FeignClient interface.

In the above code, you can see that the service name, interface type, and access path have been declared through annotations.

OpenFeign generates a "dynamic proxy class" by parsing these annotation tags. This proxy class will convert the interface call into a Request for remote service calls and send it to the target service.

So how does OpenFeign's dynamic proxy work? Next, I will take you to understand the process behind this in depth.

We have created a high-quality technical exchange group. When you are with excellent people, you will become excellent yourself. Hurry up and click to join the group and enjoy the joy of growing together.

Dynamic Proxy for OpenFeign

In the project initialization phase, OpenFeign will generate a proxy class to dynamically proxy all remote calls initiated through this interface. I drew a flowchart to help you understand OpenFeign's dynamic proxy process:

f05e503e014cefd817a9f46bbac439d3.png

Steps 1 to 3 in the above figure are loaded and completed during the project startup phase, and only step 4 "calling remote services" occurs during the running phase of the project.

Let me explain several key steps in the above figure.

First, during the project startup phase, the OpenFeign framework will initiate an active package scanning process, scanning and loading all interfaces decorated with @FeignClient annotations from the specified directory.

Then, OpenFeign will generate a dynamic proxy object for each FeignClient interface, that is, FeignProxyService in the figure. This proxy object belongs to the instance of the interface modified by the FeignClient annotation in terms of inheritance.

Next, this dynamic proxy object will be added to the Spring context and injected into the corresponding service, which is the LocalService service in the figure.

Finally, the LocalService initiates the underlying method call. In fact, this method call will be taken over by the proxy object generated by OpenFeign, which initiates a remote service call and returns the result of the call to LocalService.

I guess you must be curious: How does OpenFeign create proxy objects through dynamic proxy technology? I drew a flowchart to help you sort out this process, you can refer to it.

f09e9bb743df8f54c0885e26623ca665.png

I've drawn the important stages of the OpenFeign component loading process in the image above. Next, I will take you through the creation process of the OpenFeign dynamic proxy class.

  1. Project loading: In the startup phase of the project, the EnableFeignClients annotation plays the role of a "startup switch". It uses the Import annotation of the Spring framework to import the FeignClientsRegistrar class and starts the loading process of the OpenFeign component.

  2. Scan package: FeignClientsRegistrar is responsible for loading FeignClient interface, it will scan all FeignClients classes under the specified package path, and construct FeignClientFactoryBean object to resolve FeignClient interface.

  3. Parsing FeignClient annotations: FeignClientFactoryBean has two important functions, one is to parse the configuration information of the request path and downgrade function in the FeignClient interface; the other is to trigger the construction process of the dynamic proxy. Among them, the dynamic proxy construction is completed by the next layer of ReflectiveFeign.

  4. Building a dynamic proxy object: ReflectiveFeign contains the core logic of OpenFeign dynamic proxy, and it is mainly responsible for creating a dynamic proxy object of the FeignClient interface. ReflectiveFeign has two important tasks in this process. One is to parse the annotations at each method level on the FeignClient interface, and encapsulate the remote interface URL, interface type (GET, POST, etc.), each request parameter, etc. into metadata, and provide Each method generates a corresponding MethodHandler class as a method-level proxy; another important task is to further encapsulate these MethodHandler method proxies, and construct a dynamic proxy object that implements the InvocationHandler interface through the Java standard dynamic proxy protocol. This dynamic proxy object is bound to the FeignClient interface. In this way, all calls that occur on the FeignClient interface will eventually be undertaken by the dynamic proxy object behind it.

The construction process of MethodHandler involves complex metadata parsing. The OpenFeign component encapsulates various annotations on the FeignClient interface into metadata, and uses these metadata to "translate" a method call into a remote call Request.

So how is the "metadata analysis" mentioned above done?

It relies on the Contract protocol parsing function in the OpenFeign component. Contract is the top-level abstract interface defined in the OpenFeign component. It has a series of concrete implementations. Among them, the SpringMvcContract class is related to our actual combat project. We can see from the name of this class that it is specially used to parse Spring MVC Labeled.

The inheritance structure of SpringMvcContract is SpringMvcContract->BaseContract->Contract. Here I take a piece of SpringMvcContract code to help you understand how it parses annotations into metadata. The main function of this code is to parse the Spring MVC annotations defined on the FeignClient method level.

// 解析FeignClient接口方法级别上的RequestMapping注解
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
   // 省略部分代码...
   
   // 如果方法上没有使用RequestMapping注解,则不进行解析
   // 其实GetMapping、PostMapping等注解都属于RequestMapping注解
   if (!RequestMapping.class.isInstance(methodAnnotation)
         && !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
      return;
   }

   // 获取RequestMapping注解实例
   RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
   // 解析Http Method定义,即注解中的GET、POST、PUT、DELETE方法类型
   RequestMethod[] methods = methodMapping.method();
   // 如果没有定义methods属性则默认当前方法是个GET方法
   if (methods.length == 0) {
      methods = new RequestMethod[] { RequestMethod.GET };
   }
   checkOne(method, methods, "method");
   data.template().method(Request.HttpMethod.valueOf(methods[0].name()));

   // 解析Path属性,即方法上写明的请求路径
   checkAtMostOne(method, methodMapping.value(), "value");
   if (methodMapping.value().length > 0) {
      String pathValue = emptyToNull(methodMapping.value()[0]);
      if (pathValue != null) {
         pathValue = resolve(pathValue);
         // 如果path没有以斜杠开头,则补上/
         if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
            pathValue = "/" + pathValue;
         }
         data.template().uri(pathValue, true);
         if (data.template().decodeSlash() != decodeSlash) {
            data.template().decodeSlash(decodeSlash);
         }
      }
   }

   // 解析RequestMapping中定义的produces属性
   parseProduces(data, method, methodMapping);

   // 解析RequestMapping中定义的consumer属性
   parseConsumes(data, method, methodMapping);

   // 解析RequestMapping中定义的headers属性
   parseHeaders(data, method, methodMapping);
   data.indexToExpander(new LinkedHashMap<>());
}

Through the above method, we can see that OpenFeign has analyzed each attribute of the RequestMappings annotation.

If you use annotations such as GetMapping and PostMapping in your project, but do not use RequestMapping, can OpenFeign still parse it? sure. Take GetMapping as an example, it encapsulates the RequestMapping annotation. If you look at the code below about the GetMapping annotation, you will find that a RequestMapping annotation is also hung on the annotation header. So OpenFeign can correctly identify GetMapping and finish loading.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
// ...省略部分代码
}

Summarize

Today you understand the problems that OpenFeign wants to solve, and I also take you to understand the workflow of OpenFeign, which focuses on the dynamic proxy mechanism. OpenFeing generates a "proxy class" through Java dynamic proxy, which converts the interface call into a remote service call.

------

We have created a high-quality technical exchange group. When you are with excellent people, you will become excellent yourself. Hurry up and click to join the group and enjoy the joy of growing together. In addition, if you want to change jobs recently, I spent 2 weeks a year ago collecting a wave of face-to-face experience from big factories. If you plan to change jobs after the festival, you can click here to claim it !

recommended reading

··································

Hello, I am DD, a programmer. I have been developing a veteran driver for 10 years, MVP of Alibaba Cloud, TVP of Tencent Cloud. From general development to architect to partner. Along the way, my deepest feeling is that we must keep learning and pay attention to the frontier. As long as you can persevere, think more, complain less, and work hard, it will be easy to overtake on corners! So don't ask me if it's too late to do what I do now. If you are optimistic about something, you must persevere to see hope, not to persevere only when you see hope. Believe me, as long as you stick to it, you will be better than now! If you have no direction yet, you can follow me first, and I will often share some cutting-edge information here to help you accumulate capital for cornering and overtaking.

Guess you like

Origin blog.csdn.net/j3T9Z7H/article/details/130895745