Daniel Kelley
Daniel Kelley

Reputation: 7737

Define different Feign client implementations based on environment

I have a Spring boot application which uses Feign to call an external web service via Eureka. I'd like to be able to run the application using a mocked out implementation of the Feign interface, so I can run the application locally without necessarily having Eureka or the external web service running. I had imagined defining a run configuration that allowed me to do this, but am struggling to get this working. The issue is that the Spring "magic" is defining a bean for the Feign interface no matter what I try.

Feign interface

@FeignClient(name = "http://foo-service")
public interface FooResource {
    @RequestMapping(value = "/doSomething", method = GET)
    String getResponse();
}

Service

public class MyService {
    private FooResource fooResource;

    ...

    public void getFoo() {
        String response = this.fooResource.getResponse();
        ...
    }
}

I tried adding a configuration class that conditionally registered a bean if the Spring profile was "local", but that was never called when I ran the application with that Spring profile:

@Configuration
public class AppConfig {
    @Bean
    @ConditionalOnProperty(prefix = "spring.profile", name = "active", havingValue="local")
    public FooResource fooResource() {
        return new FooResource() {
            @Override
            public String getResponse() {
                return "testing";
            }
        };
    }
}

At the point my service runs, the FooResource member variable in MyService is of type

HardCodedTarget(type=FoorResource, url=http://foo-service)

according to IntelliJ. This is the type that is automatically generated by the Spring Cloud Netflix framework, and so tries to actually communicate with the remote service.

Is there a way I can conditionally override the implementation of the Feign interface depending on a configuration setting?

Upvotes: 9

Views: 9808

Answers (3)

baron.wang
baron.wang

Reputation: 361

the solution is like below:

public interface FeignBase {
   @RequestMapping(value = "/get", method = RequestMethod.POST, headers = "Accept=application/json")
   Result get(@RequestBody Token common);
}

then define your env based interface:

@Profile("prod")
@FeignClient(name = "service.name")
public interface Feign1 extends FeignBase 
{}
@Profile("!prod")
@FeignClient(name = "service.name", url = "your url")
public interface Feign2 extends FeignBase 
{}

finally, in your service impl:

@Resource
private FeignBase feignBase;

Upvotes: 9

David Canós
David Canós

Reputation: 1984

I'm using a simpler solution to avoid having multiples interfaces for a variable parameter like url.

    @FeignClient(name = "service.name", url = "${app.feign.clients.url}")
    public interface YourClient{}

application-{profile}.properties

app.feign.clients.url=http://localhost:9999

Upvotes: 1

Daniel Kelley
Daniel Kelley

Reputation: 7737

Having posted the same question on the Spring Cloud Netflix github repository, a useful answer was to use the Spring @Profile annotation.

I created an alternative entry point class that was not annotated with @EnabledFeignClients, and created a new configuration class that defined implementations for my Feign interfaces. This now allows me to run my application locally without the need to have Eureka running, or any dependent services.

Upvotes: 3

Related Questions