Our project is a very large application, need to divide a service to several cluster. e.g. CustomerService needs to be separated to CustomerService001, CustomerService002... Then the gateway will dispatch requests to different cluster according some logic. While the FeignClientFactoryBean is package level and cannot be extended.

Expected solution like 1. Make FeignClientFactoryBean public, so that we can override it. 2. In FeignClientBuilder, change "this.feignClientFactoryBean = new FeignClientFactoryBean();" to @Autowired so that we can provide our customized FeignClientFactoryBean. 3. Make the Targeter to public.

My final requirement is that to override the Target.url(), so that we can route requests to different service clusters. e.g. the original url is http://customerservice${clusterId}/service-api, if customer number is between 0 to 100M, then route request to http://customerservice001/service-api

Comment From: johnny2002

suggested code illustrate: 1. Make FeignClientFactoryBean to be public: **public** class FeignClientFactoryBean ... 2. Inject customized FeignClientFactoryBean instead of create a default one: `
public static final class Builder {

    @Autowired private FeignClientFactoryBean feignClientFactoryBean;


    private Builder(final ApplicationContext applicationContext, final Class<T> type,
            final String name) {
                  //this.feignClientFactoryBean = new FeignClientFactoryBean();

3. make the Targeter to public: public interface Targeter ... `

Comment From: johnny2002

I also found another issue is the same request, while has been closed. please see https://github.com/spring-cloud/spring-cloud-openfeign/issues/133

Comment From: matt62king

You should be able to do that with an interceptor so bean factory does need to be exposed.

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RequestInterceptor cloudContextInterceptor() {
        return template -> template.target( "http://customerservice${clusterId}/service-api"
                         .replaceFirst("{clusterId}", resolvedCuster()));
}

Make sure to have a value in url field of @FeignClient

Comment From: johnny2002

You should be able to do that with an interceptor so bean factory does need to be exposed.

@Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public RequestInterceptor cloudContextInterceptor() { return template -> template.target( "http://customerservice${clusterId}/service-api" .replaceFirst("{clusterId}", resolvedCuster())); }

Make sure to have a value in url field of @FeignClient

Thanks very much for your reply. This is really a good place to revise the uri here. While, I need the target object to determine whether I should change the URL. But I cannot get the target object in this interceptor. Any idea?

Comment From: matt62king

Just out of curiosity, it the target the only place that has you customer number?

We do a similar operation to route from cloud to enterprise servers. In our case with get a id out the header from the incoming request and use that get to the correct server. Could something like that work for you. Basically could the logic you want to add to FeignClientFactoryBean, be add at the Interceptor level?

Also tempate.feignTarget() will get the Target instance. The method is marked as @Experimental so???

Comment From: johnny2002

Just out of curiosity, it the target the only place that has you customer number?

We do a similar operation to route from cloud to enterprise servers. In our case with get a id out the header from the incoming request and use that get to the correct server. Could something like that work for you. Basically could the logic you want to add to FeignClientFactoryBean, be add at the Interceptor level?

Also tempate.feignTarget() will get the Target instance. The method is marked as @Experimental so???

Thanks for your reply.

If tempate.feignTarget() can get the Target instance, then everything is perfect. While the most important thing is that the target instance must not be null in RequestInterceptor.apply() method.

Comment From: johnny2002

Just out of curiosity, it the target the only place that has you customer number?

We do a similar operation to route from cloud to enterprise servers. In our case with get a id out the header from the incoming request and use that get to the correct server. Could something like that work for you. Basically could the logic you want to add to FeignClientFactoryBean, be add at the Interceptor level?

Also tempate.feignTarget() will get the Target instance. The method is marked as @Experimental so???

Dear matt62king, I found there is a "target" property in Template, while, it is null before it goes to these Interceptors, if you can set the target value before come into Interceptors, and provide a getter, then the problem will be resolved perfectly.

Comment From: johnny2002

So my suggested code illustrate:

In RequestTemplate, add:

` public String target() { return target; }

### Then, the most important thing is that the target instance must not be null in RequestInterceptor.apply() method, so in SynchronousMethodHandler, add a line:

public Object invoke(Object[] argv) throws Throwable { RequestTemplate template = buildTemplateFromArgs.create(argv); template.target(this.target.name());

Then in my interceptor:

@Bean
public RequestInterceptor cloudContextInterceptor() {
    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate template) {
            String target = template.target();
            if (target.contains("$CLUSTER_ID")) {
                target = target.replace("$CLUSTER_ID", getClusterId(template));
                template.target(target);
            }

`

Comment From: matt62king

I was wondering if it would be null at when the interceptors were called. I'm not sure if you noticed, but I put a PR for this issue.

In the mean time, can you inject the URL template into the interceptor instead of pulling it out the the target?

@Bean
public RequestInterceptor cloudContextInterceptor(SomeContext context) {
    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate template) {
            String target = someContext.getTarget();

            if (target.contains("$CLUSTER_ID")) {
                target = target.replace("$CLUSTER_ID", getClusterId(template));
                template.target(target);
            }

Comment From: johnny2002

I was wondering if it would be null at when the interceptors were called. I'm not sure if you noticed, but I put a PR for this issue.

In the mean time, can you inject the URL template into the interceptor instead of pulling it out the the target?

``` @Bean public RequestInterceptor cloudContextInterceptor(SomeContext context) { return new RequestInterceptor() { @Override public void apply(RequestTemplate template) { String target = someContext.getTarget();

      if (target.contains("$CLUSTER_ID")) {
          target = target.replace("$CLUSTER_ID", getClusterId(template));
          template.target(target);
      }

```

Yes I have noted that PR, thank you. Currently, my solution is to put the service_name in the url. While, I don't think this is an idea solution. My current code is like:

@FeignClient("customer-service")
public interface CustomerService {
    @GetMapping("//customer-service-$CLUSTER_ID/echo/{customer}")
    public String customerInfo(@PathVariable("customer") String customer);
}

then my interceptor will treat "//xxx" as service name. While, the idea soultion should be like:

@FeignClient("customer-service-$CLUSTER_ID")
public interface CustomerService {
    @GetMapping("/echo/{customer}")
    public String customerInfo(@PathVariable("customer") String customer);
}

Comment From: johnny2002

So my suggested code change of feign should like:

In RequestTemplate, add:

public String target() {
        return target;
    }

### Then, the most important thing is that the target instance must not be null in RequestInterceptor.apply() method, so in SynchronousMethodHandler, add a line:

  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    template.target(this.target.name()); //set the target value before call interceptors

Then in my interceptor:

    @Bean
    public RequestInterceptor cloudContextInterceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                String target = template.target();
                if (target.contains("$CLUSTER_ID")) {
                    target = target.replace("$CLUSTER_ID", getClusterId(template));
                    template.target(target);
                }