0

I am working on an example from here about Feign and Hystrix. Without the Feign fallback property, everything works okay. But when I add the fallback property and create the fallback class that implements the feign clients interface, I get the following error

 Description:

Field customerClient in com.feign.demo.controllers.CustomerController required a single bean, but 2 were found:
    - customerClientFallback: defined in file [../ApplicationFeign/target/classes/com/feign/demo/clients/fallback/CustomerClientFallback.class]
    - com.feign.demo.clients.CustomerClient: defined in null


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

Below is my Feign Client Interface :

@FeignClient(name = "CUSTOMERSERVICE", fallback = CustomerClientFallback.class, primary = false)
@RequestMapping(value = "customer")
public interface CustomerClient {

    @RequestMapping(method = RequestMethod.GET, value = "/getAllCustomers")
    List<Customer> getAllCustomers();

    @RequestMapping(method = RequestMethod.PATCH, value = "/{customerId}", consumes = "application/json")
    Customer update(@PathVariable("customerId") long customerId, @RequestBody Customer customer);

    @RequestMapping(method = RequestMethod.GET, value = "/{customerId}")
    Customer getCustomerById(@PathVariable("customerId") long customerId);

    @RequestMapping(method = RequestMethod.POST, value = "/", consumes = "application/json")
    Customer saveCustomer(@RequestBody Customer customer);

}

CustomerClientFallback implementation:

@Component
public class CustomerClientFallback implements CustomerClient {

    @Override
    public List<Customer> getAllCustomers() {

        return new ArrayList<Customer>();
    }

    @Override
    public Customer update(long customerId, Customer customer) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Customer getCustomerById(long customerId) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Customer saveCustomer(Customer customer) {
        // TODO Auto-generated method stub
        return null;
    }

}

Application Class:

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard
public class ApplicationFeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApplicationFeignApplication.class, args);

    }

}

Spring cloud version :

Greenwich.SR1




<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

Bellow is a modification but it does not work as well.

@RestController
public class CustomerController {

    @Autowired
    private CustomerClient customerClient;

    @Autowired
    public CustomerController(@Qualifier("customerClientFallback") CustomerClient customerClient) {
        this.customerClient = customerClient;
    }

    @RequestMapping(path = "/getAllCustomers", method = RequestMethod.GET)
    public ResponseEntity<Object> getAllCustomers() {
        List<Customer> customers = customerClient.getAllCustomers();
        return new ResponseEntity<>(customers, HttpStatus.OK);

    }

    @RequestMapping(path = "/{customerId}", method = RequestMethod.GET)
    public ResponseEntity<Object> get(@PathVariable() long customerId) {
        try {
            Customer c = customerClient.getCustomerById(customerId);
            if (c != null) {
                return new ResponseEntity<>(c, HttpStatus.OK);
            } else {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Customer Not Found");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }

    @RequestMapping(path = "/{customerId}", method = RequestMethod.PATCH)
    public ResponseEntity<Object> UpdateCustomer(@PathVariable() Long customerId, @RequestBody Customer customer) {
        Customer c;
        try {
            c = customerClient.update(customerId, customer);
            if (c != null) {
                return new ResponseEntity<>(c, HttpStatus.OK);
            } else {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Customer Not Found");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }

    @RequestMapping(path = "", method = RequestMethod.POST)
    public ResponseEntity<Object> saveCustomer(@RequestBody Customer customer) {
        Customer c;
        try {
            c = customerClient.saveCustomer(customer);
            return new ResponseEntity<>(c, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }
}
IsaacK
  • 1,178
  • 1
  • 19
  • 49

4 Answers4

0

It seems there is a problem due to using CustomerClient.java feign client in your controller class.

Please make sure you are adding qualifier

@Autowired
private CustomerClient customerClient;

@Autowired
public CustomerController(@Qualifier("customerClientFallback") CustomerClient customerClient ) {
    this.customerClient= customerClient;
}

This should works now.

I will suggest you to look into FallBackFactory for more power on feign exception handling,

Muhammad Usman
  • 863
  • 1
  • 11
  • 18
  • Same Error. Field customerClient in com.feign.demo.controllers.CustomerController required a single bean, but 2 were found: - customerClientFallback: defined in file [../microservices_ws/ApplicationFeign/target/classes/com/feign/demo/clients/fallback/CustomerClientFallback.class] - com.feign.demo.clients.CustomerClient: defined in null – IsaacK Mar 21 '19 at 15:11
  • Please attach your `CustomerController.java` class in your question – Muhammad Usman Mar 22 '19 at 04:47
  • Added at the bottom of question – IsaacK Mar 25 '19 at 09:55
0

This is known bug in Spring Cloud, see: https://github.com/spring-cloud/spring-cloud-netflix/issues/2677

Marx
  • 804
  • 10
  • 23
  • I see no solution there, I already added the enable hystrix property to enabled. Still no workaround. – IsaacK Mar 25 '19 at 10:00
0

Remove the autowired annotation from the field, you are already injecting the dependency in the constructor.

private CustomerClient customerClient;

@Autowired
public CustomerController(@Qualifier("customerClientFallback") CustomerClient customerClient) {
    this.customerClient = customerClient;
}

Its also safer to use constructor dependency injection instead of field injection - with field injection you allow anyone to create an instance of your class in an invalid state. In the constructor the dependencies are clearly specified and also it is easier to test your code (mock the dependencies and use them in the constructor)

Also, when you annotate an Interface or Class with @RequestMapping Spring will register a handler even if you have a @FeignClient annotation - and since you have an implementation of this interface you should remove it to avoid any issues with ambiguous mapping.

Like so

@FeignClient(name = "CUSTOMERSERVICE", fallback = CustomerClientFallback.class, primary = false)
public interface CustomerClient {

    @RequestMapping(method = RequestMethod.GET, value = "/getAllCustomers")
    List<Customer> getAllCustomers();

    @RequestMapping(method = RequestMethod.PATCH, value = "/{customerId}", consumes = "application/json")
    Customer update(@PathVariable("customerId") long customerId, @RequestBody Customer customer);

    @RequestMapping(method = RequestMethod.GET, value = "/{customerId}")
    Customer getCustomerById(@PathVariable("customerId") long customerId);

    @RequestMapping(method = RequestMethod.POST, value = "/", consumes = "application/json")
    Customer saveCustomer(@RequestBody Customer customer);

}
V. Lovato
  • 719
  • 4
  • 7
0

The Error seems to be because of the @RequestMapping provided at the class/interface level.

In your case because of @RequestMapping(value = "customer") in CustomerClient.java

sceint
  • 1
  • 2