SoFunction
Updated on 2025-04-05

Detailed explanation of how OpenFeign specifies the URL call method

introduction

OpenFeign is generally used in conjunction with a registry, that is, access to the target service can be accomplished by providing the name of the service instead of the URL. However, for the needs of local debugging, or considering that some simple services may not need to rely on the registration center, we will explain how OpenFeign calls directly through the URL of the target service.

FeignClient annotation configuration URL

Write a fixed call address in the url attribute of @FeignClient annotation:

package ;

import ;
import ;
import ;
import ;

import ;

/**
  * Specify the url attribute
  */
@FeignClient(value = "order-service", url = "http://localhost:8020", path = "/order", contextId = "orderUrl")
public interface OrderUrlClient {

    @GetMapping("findOrderByUserId")
    List<Order> findOrderByUserId(@RequestParam("userId") Long userId);

}

Or write a configurable address, which can be specified in the configuration file. Different URLs can be configured according to different environments. This method requires planning when creating feign client:

@FeignClient(value = "order-service", url = "${}", path = "/order", contextId = "orderUrl")
public interface OrderUrlClient {

    @GetMapping("findOrderByUserId")
    List<Order> findOrderByUserId(@RequestParam("userId") Long userId);

}

Implement the RequestInterceptor interface?

Implement the RequestInterceptor interface to modify the registration center call method to the URL call before initiating an HTTP request.

Specify the configuration property in the @FeignClient annotation, and the url property is not specified here:

package ;

import ;
import ;
import ;
import ;
import ;

import ;

@FeignClient(value = "order-service", path = "/order", contextId = "orderUrl2", configuration = )
public interface OrderUrlClient2 {

    @GetMapping("findOrderByUserId")
    List<Order> findOrderByUserId(@RequestParam("userId") Long userId);

}

FeignUrlConfig class injects a RequestInterceptor class to intercept requests in OrderUrlClient2, which will only intercept requests in OrderUrlClient2:

package ;

import ;
import ;
import ;

public class FeignUrlConfig {

    @Bean
    public  feignLoggerLevel() {
        return ;
    }

    @Bean
    public FeignUrlRequestInterceptor feignTraceRequestInterceptor() {
        return new FeignUrlRequestInterceptor();
    }
}

The FeignUrlRequestInterceptor class changes the requested address to a specific url instead of the previous serviceId.

package ;

import ;
import ;

public class FeignUrlRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        ("old: " + ()); // /findOrderByUserId?userId=1
        ("http://localhost:8020/order");
        ("new: " + ()); // http://localhost:8020/order/findOrderByUserId?userId=1
    }
}

After initiating a request, the following exception is thrown:

: Load balancer does not contain an instance for the service localhost
	at (:24) ~[classes/:na]

You can find that Feign will still go to the registration center to find services. Why is this?

By reading the FeignClientFactoryBean source code, it was found:

&lt;T&gt; T getTarget() {
  FeignContext context = ();
   builder = feign(context);

  if (!(url)) {
    if (!("http")) {
      url = "http://" + name;
    }
    else {
      url = name;
    }
    url += cleanPath();
    // The url does not exist    return (T) loadBalance(builder, context,
        new HardCodedTarget&lt;&gt;(type, name, url));
  }
  if ((url) &amp;&amp; !("http")) {
    url = "http://" + url;
  }
  String url =  + cleanPath();
  Client client = getOptional(context, );
  if (client != null) {
    if (client instanceof LoadBalancerFeignClient) {
      // not load balancing because we have a url,
      // but ribbon is on the classpath, so unwrap
      client = ((LoadBalancerFeignClient) client).getDelegate();
    }
    if (client instanceof FeignBlockingLoadBalancerClient) {
      // not load balancing because we have a url,
      // but Spring Cloud LoadBalancer is on the classpath, so unwrap
      // The url does not exist      client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
    }
    (client);
  }
  Targeter targeter = get(context, );
  return (T) (this, builder, context,
      new HardCodedTarget&lt;&gt;(type, name, url));
}

When the url attribute in @FeignClient does not exist, the underlying client uses FeignBlockingLoadBalancerClient. This client will register the center to query the service according to the serviceId and perform load balancing. Although FeignUrlRequestInterceptor has modified the url address, the client will only modify the serviceId according to the modified serviceId, that is, FeignUrlRequestInterceptor can only modify the serviceId and cannot change the call method.

When the url attribute in @FeignClient exists, the underlying client uses (), that is, ApacheHttpClient, and this client will not go to the registration center to query the service and directly initiate the interface call.

Customize FeignBlockingLoadBalancerClient

Customize a FeignBlockingLoadBalancerClient to rewrite the url:

Specify the configuration attribute in the @FeignClient annotation:

package ;

import .OrderUrlClient3Config;
import ;
import ;
import ;
import ;

import ;

@FeignClient(value = "order-service", path = "/order", contextId = "orderUrl3", configuration = )
public interface OrderUrlClient3 {

    @GetMapping("findOrderByUserId")
    List<Order> findOrderByUserId(@RequestParam("userId") Long userId);

}

OrderUrlClient3Config class injects an OrderUrlClient3Client class to intercept requests in OrderUrlClient3. This will only intercept requests in OrderUrlClient3. If you want to implement global interception, you can add @Configuration annotation to OrderUrlClient3Config class:

package ;

import ;
import ;
import ;

public class OrderUrlClient3Config {

    @Bean
    public  feignLoggerLevel() {
        return ;
    }

    @Bean
    public OrderUrlClient3Client orderUrlClient3Client() {
        return new OrderUrlClient3Client(new (null, null), null);
    }
}

The OrderUrlClient3Client class inherits FeignBlockingLoadBalancerClient and overrides the execute() method:

package ;

import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;

public class OrderUrlClient3Client extends FeignBlockingLoadBalancerClient {

    private final Client delegate;

    public OrderUrlClient3Client(Client delegate, BlockingLoadBalancerClient loadBalancerClient) {
        super(delegate, loadBalancerClient);
         = delegate;
    }

    @Override
    public Response execute(Request request,  options) throws IOException {
        final URI originalUri = (());
        // Modify the url        URI newUri = (originalUri).host("localhost").port(8020)
                .build().toUri();
        Request newRequest = ((), (),
                (), (), (),
                ());
        return (newRequest, options);
    }
}

BeanFactoryPostProcessor modify the url property of the bean

Here you can use Spring extension to add url attribute to the Bean object FeignClientFactoryBean corresponding to @FeignClient, so that the url attribute is added during the container startup process. The client created by feign is ApacheHttpClient, not FeignBlockingLoadBalancerClient

package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;

@Component
public class OrderUrl4BeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if(!(beanFactory instanceof DefaultListableBeanFactory)) {
            return;
        }

        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;

        String[] bdNames = ();
        for (String bdName : bdNames) {
            BeanDefinition beanDefinition = (bdName);

            if (!("", ())) {
                continue;
            }

            if(!(".OrderUrlClient4")) {
                // Only OrderUrlClient4 is intercepted here, and letting go is the whole world                continue;
            }

            PropertyValue urlPv = ().getPropertyValue("url");
            if ((urlPv)) {
                Object value = ();
                if (value instanceof String) {
                    String url = (String) value;
                    if ((url)) {
                        // The specified url has been skipped                        continue;
                    }
                }
            }

            // It is equivalent to adding url attribute to @FeignClinet annotation            ().addPropertyValue("url", "http://localhost:8020");
        }
    }
}

This is the article about the detailed explanation of OpenFeign's specified url call method. For more information about OpenFeign specified url call content, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!