Location>code7788 >text

OpenFeign in-depth study notes

Popularity:559 ℃/2024-09-03 12:25:59

OpenFeign is a declarative Web service client that makes it easier to write Web service clients.OpenFeign is a component in the Spring Cloud ecosystem that integrates Ribbon (a client-side load balancer) and Eureka (a service discovery component) to simplify calls between microservices.

In SpringCloud applications, we often use OpenFeign, for example, to create a web service client by defining an interface and annotating it without writing a lot of boilerplate code. OpenFeign automatically generates an implementation class for the interface and uses the Ribbon to invoke the corresponding service.

Let's get started by using OpenFeign in a Spring Cloud project:

demand (economics): Our business scenario is this: an e-commerce platform that contains a goods service (product-service) and an order service (order-service). We're going to use OpenFeign to implement an interface for the order service to call the merchandise service.

Step 1: Creating a product-service

  1. Adding Dependencies):
   <dependencies>
       <!-- Spring Boot Web dependencies -->
       <dependency>
           <groupId></groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <!-- Spring Boot Actuator dependencies -->
       <dependency>
           <groupId></groupId>
           <artifactId>spring-boot-starter-actuator</artifactId>
       </dependency>
   </dependencies>
  1. Master Application Class):
   import ;
   import ;

   @SpringBootApplication
   public class ProductApplication {
       public static void main(String[] args) {
           (, args);
       }
   }
  1. Commodity Controller):
   import ;
   import ;
   import ;

   @RestController
   public class ProductController {

       @GetMapping("/products/{id}")
       public String getProduct(@PathVariable("id") Long id) {
           // Simulate the database to get the product information
           return "Product with ID: " + id;
       }
   }

Step 2: Create an order service (order-service)

  1. Adding Dependencies):
   <dependencies>
       <!-- Spring Boot Web dependencies -->
       <dependency>
           <groupId></groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <!-- Spring Cloud OpenFeign dependencies -->
       <dependency>
           <groupId></groupId>
           <artifactId>spring-cloud-starter-openfeign</artifactId>
       </dependency>
   </dependencies>
  1. Master Application Class):
   import ;
   import ;
   import ;

   @SpringBootApplication
   @EnableFeignClients
   public class OrderApplication {
       public static void main(String[] args) {
           (, args);
       }
   }
  1. Feign Client Interface):
   import ;
   import ;
   import ;

   @FeignClient(name = "product-service", url = "http://localhost:8081")
   public interface ProductClient {
       @GetMapping("/products/{id}")
       String getProduct(@PathVariable("id") Long id);
   }
  1. Order Controller):
   import ;
   import ;
   import ;
   import ;

   @RestController
   public class OrderController {

       private final ProductClient productClient;

       @Autowired
       public OrderController(ProductClient productClient) {
            = productClient;
       }

       @GetMapping("/orders/{id}/product")
       public String getOrderProduct(@PathVariable("id") Long id) {
           // Calling the commodity service to get commodity information
           return (id);
       }
   }

Step 3: Run and test

  1. Initiation of commodity servicesProductApplication):

    • (of a computer) runProductApplication (used form a nominal expression)main Methods.
  2. Starting the order serviceOrderApplication):

    • (of a computer) runOrderApplication (used form a nominal expression)main Methods.
  3. test call

    • Accessed using a browser or Postmanhttp://localhost:8082/orders/1/productThen we can see the commodity information returned by the commodity service.

The above is the basic use of OpenFeign, as a good programmer, we must go in depth to understand the implementation behind the core components of OpenFeign, know yourself and know your enemy, so that you can fight. Here we come together to look at some of the core components of OpenFeign and its source code analysis:

What are the core components of OpenFeign?

OpenFeign is a declarative web service client in the Spring Cloud ecosystem that simplifies HTTP calls between microservices.

1. Encoder:

In OpenFeign, theEncoder The component is responsible for serializing the request data into a format that can be sent. By default, OpenFeign only supports serializing request data as strings or byte arrays. If you need to support more complex object serialization, you can do so by implementing a customEncoder to realize.

Let's analyze it.Encoder component's source code implementation:

Step 1: Define the Encoder Interface

First, Feign defines aEncoder interface, which contains aencode method for serializing an object as a byte array:

public interface Encoder {
    void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
  • object: The object to be serialized.
  • bodyType: Information about the type of the object, usually used to determine how to serialize the object.
  • templateRequestTemplate object that sets the body (body) of the request.

Step 2: Implement the Default Encoder

OpenFeign provides a defaultEncoder implementations, typically using Jackson or other JSON libraries to serialize objects into JSON format:

public class JacksonEncoder extends SpringEncoder implements Encoder {
    public JacksonEncoder(ObjectFactory objectFactory) {
        super(objectFactory);
    }

    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
        // Serialize an object as JSON specification,and set it to RequestTemplate center
        byte[] body = (bodyType).writeValueAsBytes(object);
        (body);
        ((body, ContentType.APPLICATION_JSON));
    }
}

In this realization, theJacksonEncoder Use the Jackson library to serialize the object to JSON and set the request's content type toAPPLICATION_JSON

Step 3: Customizing the Encoder Implementation

If we need to support other types of serialization, we can create customEncoder implementations. For example, to support XML serialization, you can create an implementation that uses JAXB or another XML library'sEncoder

public class XmlEncoder implements Encoder {
    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
        // utilization XML The library serializes objects as XML specification
        String xml = serializeToXml(object);
        (xml, ContentType.APPLICATION_XML);
    }

    private String serializeToXml(Object object) {
        // Realize the object to XML The serialization logic of the
        // ...
        return xml;
    }
}

Step 4: Configure the OpenFeign client to use a custom Encoder

In the Feign client configuration, you can specify a customizedEncoder Realization:

@Configuration
public class FeignConfig {
    @Bean
    public Encoder encoder() {
        return new XmlEncoder();
    }
}

after that@FeignClient The configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

To summarize.

Encoder interface, which provides an extension point for serializing request data.OpenFeign provides the defaultJacksonEncoder implementation, which uses the Jackson library to serialize objects into JSON format. If you want to implement a customEncoderIt is also very convenient and flexible to support other data formats, such as XML, Protobuf and so on.

2. Decoder:

OpenFeign'sDecoder component is responsible for deserializing HTTP response bodies (usually byte streams) into Java objects. By default, OpenFeign supports deserialization of response bodies as strings or byte arrays. If you need to support more complex object deserialization, you can do so by implementing a customDecoder to realize.

Here's an analysis.Decoder component's source code implementation:

Step 1: Define the Decoder Interface

OpenFeign defines aDecoder interface, which contains adecode method for deserializing the response body into an object:

public interface Decoder {
    <T> T decode(Response response, Type type) throws IOException, DecodeException;
}
  • T: The type of object to deserialize into.
  • response: Feign'sResponse object that contains all the information about the HTTP response.
  • type: The type information of the object to be deserialized into.

Step 2: Implement the Default Decoder

OpenFeign provides a defaultDecoder implementations, typically using Jackson or other JSON libraries to deserialize JSON response bodies:

public class JacksonDecoder extends SpringDecoder implements Decoder {
    public JacksonDecoder(ObjectFactory objectFactory) {
        super(objectFactory);
    }

    @Override
    public <T> T decode(Response response, Type type) throws IOException, DecodeException {
        // Getting a byte stream from a response
        InputStream inputStream = ().asInputStream();
        // utilization Jackson library deserializes a stream of bytes into Java boyfriend
        return (type).readValue(inputStream, type);
    }
}

In this realization, theJacksonDecoder Use the Jackson library to deserialize the byte stream of the response body into Java objects and throw exceptions or return objects depending on the status code of the response.

Step 3: Customizing the Decoder Implementation

If we need to support other types of deserialization, we can create a customizedDecoder Implementations. To support XML deserialization, for example, you can create an implementation that uses JAXB or another XML library'sDecoder

public class XmlDecoder implements Decoder {
    @Override
    public <T> T decode(Response response, Type type) throws IOException, DecodeException {
        // Getting a byte stream from a response
        InputStream inputStream = ().asInputStream();
        // utilization XML library deserializes a stream of bytes into Java boyfriend
        T object = deserializeFromXml(inputStream, type);
        return object;
    }

    private <T> T deserializeFromXml(InputStream inputStream, Type type) {
        // realization XML until (a time) Java boyfriend的反序列化逻辑
        // ...
        return null;
    }
}

Step 4: Configure the OpenFeign Client to Use a Custom Decoder

In the Feign client configuration, you can specify a customizedDecoder Realization:

@Configuration
public class FeignConfig {
    @Bean
    public Decoder decoder() {
        return new XmlDecoder();
    }
}

after that@FeignClient The configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

To summarize.

OpenFeign provides a defaultJacksonDecoder implementation, which uses the Jackson library to deserialize JSON-formatted response bodies. We can also implement a customDecoderIn addition, it can support other data formats, such as XML. In development, we can choose or implement the deserialization method suitable for their business scenarios according to their needs.

3. Contract:

OpenFeign'sContract The component is responsible for converting the methods and annotations of the interface to HTTP requests. It defines how to map Java interfaces to HTTP requests, including the request URL, HTTP methods, request headers, query parameters, and request body.Contract Components are a very central part of Feign because they determine how the Feign client understands and constructs HTTP requests.

Step 1: Define the Contract Interface

Feign defines aContract interface, which contains two main methods:

public interface Contract {
    List<MethodMetadata> parseAndValidatteMethods(FeignTarget<?> target);
    RequestTemplate create(Request request, Target<?> target, Method method, Object... argv);
}
  • parseAndValidatteMethods: parses and validates the methods of the target interface, generating theMethodMetadata list, eachMethodMetadata Contains all the metadata for a method.
  • create: Based onRequest cap (a poem)Target establishRequestTemplate, which is used to construct the actual HTTP request.

Step 2: Implement the Default Contract

Feign provides a defaultContract implementations, it is common to use Java's reflection API to resolve the interface's methods and annotations:

public class FeignContract implements Contract {
    @Override
    public List<MethodMetadata> parseAndValidateMethods(FeignTarget<?> target) {
        // Methods for parsing the target interface,generating MethodMetadata listings
        // ...
    }

    @Override
    public RequestTemplate create(Request request, Target<?> target, Method method, Object... argv) {
        // according to MethodMetadata and parameter creation RequestTemplate
        // ...
    }
}

In the realization we can see thatFeignContract will check the annotations on the interface methods (such as the@GetMapping@PostMapping etc.) and construct HTTP requests based on these annotations.

Step 3: Customizing the Contract Implementation

By the same token, if you need to support custom annotations or extend Feign's functionality, you can do so by implementing a customContract to realize:

public class MyCustomContract implements Contract {
    @Override
    public List<MethodMetadata> parseAndValidateMethods(FeignTarget<?> target) {
        // Custom parsing logic
        // ...
    }

    @Override
    public RequestTemplate create(Request request, Target<?> target, Method method, Object... argv) {
        // Custom Creation RequestTemplate logic
        // ...
    }
}

Step 4: Configure the OpenFeign client to use a custom Contract

In the Feign client configuration, you can specify a customizedContract Realization:

@Configuration
public class FeignConfig {
    @Bean
    public Contract contract() {
        return new MyCustomContract();
    }
}

after that@FeignClient The configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

To summarize.

OpenFeign provides a defaultFeignContract implementation, which uses Java's reflection API to parse the interface's methods and annotations and generate theMethodMetadata. This approach allows Feign to automatically handle standard JAX-RS annotations. We can do this by implementing a customContract, which can support custom annotations or change Feign's request building logic.

4. Client:

Client component is the core component responsible for sending HTTP requests and receiving HTTP responses.OpenFeign defaults to the Java Standard Library'sHttpURLConnectionto send requests, but also supports the use of other HTTP client libraries such as Apache HttpClient or OkHttp.

Step 1: Define the Client Interface

OpenFeign defines aClientinterface, which contains aexecutemethod for performing HTTP requests:

public interface Client {
    Response execute(Request request,  options) throws IOException;
}
  • Request: Represents the HTTP request to be executed.
  • : Contains requested configuration options such as connection timeout and read timeout.
  • Response: Represents the HTTP response.

Step 2: Implement the Default Client

OpenFeign provides a defaultClientimplementation, using Java'sHttpURLConnection

public class HttpURLConnectionClient implements Client {
    @Override
    public Response execute(Request request, options) throws IOException {
        // Create and Configure HttpURLConnection
        URL url = new URL(());
        HttpURLConnection connection = (HttpURLConnection) ();
        // Setting the request method、header (computing)、timeout etc.
        // ...
        // Send a request and get a response
        // ...
        return response;
    }
}

In this realization, theHttpURLConnectionClientutilizationHttpURLConnectionto build and send HTTP requests and process responses.

Step 3: Customizing the Client Implementation

If we need to use other HTTP client libraries, we can create customizedClientimplementations. For example, use Apache HttpClient:

public class HttpClientClient implements Client {
    private final CloseableHttpClient httpClient;

    public HttpClientClient(CloseableHttpClient httpClient) {
         = httpClient;
    }

    @Override
    public Response execute(Request request, options) throws IOException {
        // utilization Apache HttpClient Build and SendHTTPrequesting
        // ...
        return response;
    }
}

Step 4: Configure the Feign Client to Use a Custom Client

In the Feign configuration, you can specify a customizedClientRealization:

@Configuration
public class FeignConfig {
    @Bean
    public Client httpClientClient() {
        CloseableHttpClient httpClient = ();
        return new HttpClientClient(httpClient);
    }
}

after that@FeignClientThe configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

To summarize.

OpenFeign provides a defaultHttpURLConnectionClientimplementation, which uses the Java Standard Library'sHttpURLConnectionto send the request. The advantage of this approach is that it is simple and requires no additional dependencies. It is also possible to send a request by implementing a customClientThis allows OpenFeign to be adapted to different performance and functionality needs .

5. RequestInterceptor:

RequestInterceptor It is a very important component that allows us to intercept requests before they are sent, so that we can add some general processing logic, such as setting authentication headers, logging, modifying request parameters, etc. Let's analyzeRequestInterceptor component's source code implementation:

Step 1: Define the RequestInterceptor Interface

Feign defines aRequestInterceptor interface, which contains aapply method before the request is sent.RequestTemplate Perform the operation:

public interface RequestInterceptor {
    void apply(RequestTemplate template);
}
  • RequestTemplate: Represents the upcoming HTTP request and can modify the URL, headers, request body, and so on.

Step 2: Implement the Default RequestInterceptor

OpenFeign provides some defaultRequestInterceptor implementations, such as those used to set default headers forRequestInterceptor

public class DefaultRequestInterceptor implements RequestInterceptor {
    private final Configuration configuration;

    public DefaultRequestInterceptor(Configuration configuration) {
         = configuration;
    }

    @Override
    public void apply(RequestTemplate template) {
        // Setting the default header information,for exampleContent-Type
        ("Content-Type", ().toString());
        // More default processing logic can be added
    }
}

In this realization, theDefaultRequestInterceptor will set some default headers on each request.

Step 3: Customizing the RequestInterceptor Implementation

We can implement customizedRequestInterceptor. For example. add an interceptor for setting authentication headers:

public class AuthRequestInterceptor implements RequestInterceptor {
    private final String authToken;

    public AuthRequestInterceptor(String authToken) {
         = authToken;
    }

    @Override
    public void apply(RequestTemplate template) {
        // Add authentication headers to each request
        ("Authorization", "Bearer " + authToken);
    }
}

Step 4: Configure the OpenFeign Client to Use a Custom RequestInterceptor

In the OpenFeign configuration, you can specify a customizedRequestInterceptor Realization:

@Configuration
public class FeignConfig {
    @Bean
    public RequestInterceptor authRequestInterceptor() {
        // Assuming that the authentication token is obtained from a configuration file or environment variable
        String authToken = "your-auth-token";
        return new AuthRequestInterceptor(authToken);
    }
}

after that@FeignClient The configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

To summarize.

RequestInterceptor allows us to unify the processing of requests without modifying each individual request. This makes the Feign client more flexible and powerful, able to adapt to a variety of complex business needs.

6. Retryer:

Retryer The component is responsible for defining the retry policy, which determines whether or not to retry a request when a specific type of error is encountered, as well as the number and interval of retries.Retryer Feign is an important component , especially in the network instability or service instability of the environment , come in handy , it can significantly improve the robustness of the system oh .

Step 1: Define the Retryer Interface

Feign defines aRetryer interface, which contains several key methods for controlling the behavior of retries:

public interface Retryer {
    void continueOrPropagate(RetryableException e);
    Retryer clone();
    long getDelay(RetryableException e, int attempt);
    boolean shouldRetry(RetryableException e, int attempt, int retry);
}
  • continueOrPropagate: Decide whether to continue retrying or throw an exception.
  • clone: CreateRetryer copy of the request, which is typically used for a per-request independent retry policy.
  • getDelay: Returns the delay before the next retry.
  • shouldRetry: Decide whether the request should be retried.

Step 2: Implement the Default Retryer

Feign provides a defaultRetryer implementation, usually a simple retry strategy, for example:

public class DefaultRetryer implements Retryer {
    private final long period;
    private final long maxPeriod;
    private final int maxAttempts;

    public DefaultRetryer(long period, long maxPeriod, int maxAttempts) {
         = period;
         = maxPeriod;
         = maxAttempts;
    }

    @Override
    public void continueOrPropagate(RetryableException e) {
        // Determine whether to retry based on exception type and number of retries
        if (shouldRetry(e, (), maxAttempts)) {
            // Keep retrying.
        } else {
            // throw an exception
            throw e;
        }
    }

    @Override
    public Retryer clone() {
        return new DefaultRetryer(period, maxPeriod, maxAttempts);
    }

    @Override
    public long getDelay(RetryableException e, int attempt) {
        // Calculating Retry Latency
        return (period * (long) (2, attempt), maxPeriod);
    }

    @Override
    public boolean shouldRetry(RetryableException e, int attempt, int retry) {
        // Determine whether to retry based on exception type and number of retries
        return attempt < retry;
    }
}

In this realization, theDefaultRetryer Uses an exponential backoff policy to calculate retry latency and allows the maximum number of retries to be specified.

Step 3: Customizing the Retryer Implementation

When we need a more complex retry policy, we can create customizedRetryer Implementation. For example, a retry strategy may be determined based on a particular exception type or response code:

public class CustomRetryer implements Retryer {
    // ... Customizing Retry Logic ...

    @Override
    public void continueOrPropagate(RetryableException e) {
        // Customizing Retry Logic
    }

    @Override
    public Retryer clone() {
        return new CustomRetryer();
    }

    @Override
    public long getDelay(RetryableException e, int attempt) {
        // Customizing delay logic
        return ...;
    }

    @Override
    public boolean shouldRetry(RetryableException e, int attempt, int retry) {
        // Customizing retry conditions
        return ...;
    }
}

Step 4: Configure the Feign Client to Use a Custom Retryer

In the Feign configuration, you can specify a customizedRetryer Realization:

@Configuration
public class FeignConfig {
    @Bean
    public Retryer retryer() {
        return new CustomRetryer();
    }
}

after that@FeignClient The configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

To summarize.

OpenFeign allows us to choose or implement retry policies that fit our business scenarios as needed, thus improving the robustness and reliability of the system. The question is, what is an exponential backoff strategy?

To explain ha, Exponential Backoff is a retry strategy commonly used in network communication and distributed systems, especially when dealing with temporary failures or network delays. This strategy aims to reduce the load on the system by increasing the waiting time between consecutive retries and improving the chances of a successful retry.

The exponential backoff strategy works like this: when an error or failure occurs, the system first waits for an initial short delay and then retries. If the first retry fails, the wait time grows exponentially. This means that the wait time for each retry is twice as long as the previous one (or another exponential factor).

A maximum number of attempts is usually set to prevent infinite retries. To avoid long waiting time, a maximum delay time is set, after which the waiting time will not be increased even if the maximum number of retries is not reached.

To reduce the synchronization effect when multiple clients retry at the same time, a randomization factor is sometimes added to the exponential backoff so that the wait time varies within a certain range each time.

7. Configuration:

Configuration Component is a key setup class that allows users to customize the behavior of the Feign client.Configuration Classes usually contain a set of settings, such as connection timeout, read timeout, retry policy, encoder, decoder, contract, logging level, and so on. These settings can be applied to all Feign clients, or to specific Feign clients.

Step 1: Define the Configuration Interface

Feign defines aConfiguration interface, which allows the user to configure various parameters of the Feign client:

public interface Configuration {
    // Returns the configured encoder
    Encoder encoder(); // Return the configured encoder.

    // Returns the configured decoder
    Decoder decoder(); // Returns the configured decoder.

    // Returns the configured contract
    Contract contract().

    // Returns the configured request interceptor
    RequestInterceptor requestInterceptor(); // Returns the configured request interceptor.

    // Returns the configured retry policy
    Retryer retryer(); // Returns the configured retry policy.

    // Returns the configured logger level
     loggerLevel(); // Returns the configured logging level.

    // ... There may be other configuration methods ...
}

Step 2: Implement the Default Configuration

Feign provides a defaultConfiguration implementation, this implementation contains the default behavior of Feign:

public class DefaultConfiguration implements Configuration {
    // ... Define the default encoder、codec、compacts ...

    @Override
    public Encoder encoder() {
        return new JacksonEncoder(...);
    }

    @Override
    public Decoder decoder() {
        return new JacksonDecoder(...);
    }

    @Override
    public Contract contract() {
        return new SpringMvcContract(...);
    }

    // ... Implementing Other Configuration Methods ...
}

In this realization, theDefaultConfiguration Defines the default encoder, decoder, contract, and other components of Feign.

Step 3: Custom Configuration Implementation

Users can create customizedConfiguration implementation to override the default behavior:

public class CustomConfiguration extends DefaultConfiguration {
    // ... Customize a specific configuration ...

    @Override
    public Encoder encoder() {
        // Return the custom encoder
        return new CustomEncoder(...) ;
    }

    @Override
    public Decoder decoder() {
        // Return a custom decoder
        return new CustomDecoder(...) ;
    }

    // ... Can override other configuration methods ...
}

Step 4: Configure the Feign client to use a custom Configuration

In the Feign configuration, you can specify a customizedConfiguration Realization:

@Configuration
public class FeignConfig {
    @Bean
    public Configuration feignConfiguration() {
        return new CustomConfiguration(...);
    }
}

after that@FeignClient The configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

8. Target:

Target The component represents the target of the remote service that the Feign client will invoke. It usually contains the name of the service and possibly a specific configuration, such as the URL of the request.Target The component plays an important role in Feign's dynamic proxy mechanism because it defines how method calls are converted into actual HTTP requests.

Step 1: Define the Target Interface

Feign defines aTarget interface, which contains some key methods and properties:

public interface Target<T> {
    String name();

    String url();

    Class<T> type();
}
  • name(): Returns the name of the service, usually used for service discovery.
  • url(): Returns the URL of the service, either the full URL or a template.
  • type(): Returns the type of the Feign client interface.

Step 2: Implement the Target interface

Feign providesTarget An implementation of an interface, usually a file namedHardCodedTarget The class:

public class HardCodedTarget<T> implements Target<T> {
    private final String name;
    private final String url;
    private final Class<T> type;

    public HardCodedTarget(Class<T> type, String name, String url) {
         = type;
         = name;
         = url;
    }

    @Override
    public String name() {
        return name;
    }

    @Override
    public String url() {
        return url;
    }

    @Override
    public Class<T> type() {
        return type;
    }
}

In this realization, theHardCodedTarget Receive the name, URL, and interface type of the service through the constructor and provide the corresponding getter methods.

Step 3: Using the Target Component

In the Feign client interface, you can pass the@FeignClient annotatedvalue attribute specifies the name of the service, Feign will internally use theTarget to build the agent:

@FeignClient(value = "myService", url = "http://localhost:8080")
public interface MyClient extends MyServiceApi {
    // Define the service methods
}

Feign will use the annotation information to create aHardCodedTarget instance and use it to build the dynamic agent.

Step 4: Dynamic Agents and Targets

Feign uses Java's dynamic proxy mechanism (is not everywhere is a dynamic proxy, so that dynamic proxy is important) to create client-side proxy. In Feign'sReflectiveFeign class will use theInvocationHandlerFactory to createInvocationHandler

public class ReflectiveFeign extends Feign {
    private final InvocationHandlerFactory invocationHandlerFactory;

    public ReflectiveFeign(InvocationHandlerFactory invocationHandlerFactory) {
         = invocationHandlerFactory;
    }

    @Override
    public <T> T newInstance(Target<T> target) {
        // utilization InvocationHandlerFactory establish InvocationHandler
        InvocationHandler invocationHandler = (target);
        // establish动态代理
        return (T) (().getClassLoader(),
                new Class<?>[]{()}, invocationHandler);
    }
}

In the process.InvocationHandler know how to useTarget to build the actual HTTP request.

9. InvocationHandlerFactory:

InvocationHandlerFactory component is the core of the dynamic agent and is responsible for creating theInvocationHandler, which is a key part of Java's dynamic proxy mechanism.InvocationHandler defines the behavior of the proxy object when it is called. In the context of Feign, theInvocationHandler Responsible for converting method calls into HTTP requests.

Step 1: Define the InvocationHandlerFactory interface

Feign defines aInvocationHandlerFactory interface, which contains a method to create theInvocationHandler

public interface InvocationHandlerFactory {
    InvocationHandler create(Target target);
}
  • Target: Represents the target of the Feign client and contains the name, URL, and interface type of the service.

Step 2: Implement InvocationHandlerFactory

Feign provides a defaultInvocationHandlerFactory implementation, which is usually a file namedReflectiveInvocationHandlerFactory The class:

public class ReflectiveInvocationHandlerFactory implements InvocationHandlerFactory {
    @Override
    public InvocationHandler create(Target target) {
        return new ReflectiveInvocationHandler(target);
    }
}

In this realization, theReflectiveInvocationHandlerFactory Creates aReflectiveInvocationHandler instance, thisInvocationHandler will handle reflection calls.

Step 3: Implement InvocationHandler

ReflectiveInvocationHandler beInvocationHandler An implementation of the interface that is responsible for converting method calls into HTTP requests:

public class ReflectiveInvocationHandler implements InvocationHandler {
    private final Target<?> target;
    private final FeignClientFactory feignClientFactory;

    public ReflectiveInvocationHandler(Target<?> target, FeignClientFactory feignClientFactory) {
         = target;
         = feignClientFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Constructed from methods and parametersRequestTemplate
        RequestTemplate template = buildTemplateFromArgs(target, method, args);
        // Send a request and get a response
        Response response = (target).execute(template, options());
        // Construct the return value from the response
        return decode(response, ());
    }

    private RequestTemplate buildTemplateFromArgs(Target<?> target, Method method, Object[] args) {
        // Building request template logic
        // ...
    }

    private options() {
        // Get request options,If the timeout is set
        // ...
    }

    private Object decode(Response response, Type type) {
        // Logic to decode the response into the method return type
        // ...
    }
}

In the code we find that theinvoke method is the core, which constructs a target object, method, and arguments based on theRequestTemplateand then use theFeignClient Sends a request and gets a response.

Step 4: Configure the Feign Client to Use a Custom InvocationHandlerFactory

customizableInvocationHandlerFactory, which can be specified in the Feign configuration:

@Configuration
public class FeignConfig {
    @Bean
    public InvocationHandlerFactory invocationHandlerFactory() {
        return new CustomInvocationHandlerFactory();
    }
}

after that@FeignClient The configuration class is specified in the annotation:

@FeignClient(name = "myClient", configuration = )
public interface MyClient {
    // ...
}

To summarize.

Why do I say that programmers need to fully understand the application of design patterns? If you are asked any questions about design patterns in an interview, please don't just talk about the concepts, don't just talk about the concepts, don't just talk about the concepts, don't just talk about the concepts, but also combine it with the business scenarios, or combine it with the understanding of the framework source code to speak, and talk about what problems are solved, and this is the key to it.

ultimate

OpenFeign is a powerful tool in the Spring Cloud ecosystem that makes communication between microservices easier and more efficient. By using OpenFeign, developers can focus on the implementation of the business logic , and do not need to care about the underlying HTTP communication details. Welcome to pay attention to Wei brother love programming, original is not easy, ask for a praise ah brother.