Thursday, February 22, 2024

Spring Boot Microservice Example Using FeignClient

In the post Spring Boot Microservice - Service Registration and Discovery With Eureka we have seen an example of using Eureka for Service registration and discovery. In that example RestTemplate is used for inter-service communication. In this post we'll see how to use FeignClient for communication between microservices.

Feign Client

Feign client is a declarative REST client. You create an interface and annotate it with @FeignClient annotation. Feign creates a dynamic implementation of this interface at run time. Two annotations that are used with FeignClient are-

  1. @EnableFeignClients- To be used with the application class. With this annotation applied framework scans for interfaces that declare they are feign clients.
  2. @FeignClient- To be used with the interfaces declaring that a REST client with that interface should be created, which can then be autowired into another component.

Spring Boot Microservice with FeignClient example

We'll use the example used in Spring Boot Microservice - Service Registration and Discovery With Eureka as our base example and made changes to it to use FeignClient in place of RestTemplate.

In this Microservice example we'll have two services CustomerService and AccountService to cater to customer and account related functionality respectively. When customer information is retrieved it should also get the accounts associated with the specific customer. For getting the associated accounts for a customer we'll have to make a call from CustomerService to AccountService. That way we'll also see communication between Microservice.

Add maven dependency

To use FeignClient, Spring Cloud OpenFeign is needed so you need to add spring-cloud-starter-openfeign to your pom.xml. That should be done in CustomerService as this is the service from where we need to communicate with AccountService.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

Using @EnableFeignClients annotation

To make your application aware of the feign clients you need to use @EnableFeignClients annotation in the application class. Then the framework scans the interfaces that are created as feign clients.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@EnableFeignClients
@ComponentScan(basePackages = "com.netjstech.customerservice")
public class CustomerServiceApplication {

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

Creating interface annotated with @FeignClient annotation

With in the Customer-Service create an interface AccountFeignClient.

import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.netjstech.customerservice.dto.AccountDto;

@FeignClient("account-service")
public interface AccountFeignClient {
  @GetMapping("/account/customer/{id}")
  public List<AccountDto> getAccountsByCustomerId(@PathVariable("id") Long customerId);
}

In the @FeignClient annotation the String value ("account-service" in our example) is an arbitrary client name, which is used to create a Spring Cloud LoadBalancer client. The load-balancer client above will want to discover the physical addresses for the "account-service" service. If your application is a Eureka client then it will resolve the service in the Eureka service registry.

We already have another service account-service registered as Eureka client.

OpenFeign FeignClient

At runtime this interface annotated with @FeignClient annotation creates a proxy implementation which calls the AccountController with this path /account/customer/{id}

Which means the actual method called is getAccountsByCustomerId(@PathVariable("id") Long customerId) in the AccountController class.

@RestController
@RequestMapping("/account")
public class AccountController {
  private final AccountService accountService;
  
  AccountController(AccountService accountService){
    this.accountService = accountService;
  }
  
  @PostMapping
  public ResponseEntity<Account> saveAccount(@RequestBody Account account) {
    return ResponseEntity.ok(accountService.saveAccount(account));
  }
  
  @GetMapping("/{accountNumber}")
  public ResponseEntity<Account> getAccountByAccountNumber(@PathVariable("accountNumber") String accountNumber){
    System.out.println("id .. " + accountNumber);
    Account account = accountService.getAccountByAccountNumber(accountNumber);
    
    return ResponseEntity.ok(account);
  }
  
  @GetMapping("/customer/{id}")
  public List<Account> getAccountsByCustomerId(@PathVariable("id") Long customerId){
    System.out.println("id .. " + customerId);
    List<Account> accounts = accountService.getAccountsByCustomerId(customerId);
    
    return accounts;
  }
}

Changes in CustomerService implementation

Make changes in the CustomerService to use the AccountFeignClient.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.netjstech.customerservice.dao.entity.Customer;
import com.netjstech.customerservice.dao.repository.CustomerRepository;
import com.netjstech.customerservice.dto.AccountDto;
import com.netjstech.customerservice.dto.CustomerDto;
import com.netjstech.customerservice.exception.CustomerServiceException;

@Service
public class CustomerServiceFeignImpl implements CustomerService {
  
  @Autowired
  private CustomerRepository customerRepository;
  @Autowired
  private AccountFeignClient accountFeignClient;
  @Override
  public Customer saveCustomer(Customer customer) {
    return customerRepository.save(customer);
  }

  @Override
  public CustomerDto getCustomerById(Long id) {
    System.out.println("Customer id from feign impl- " + id);
    Customer customer = customerRepository.findById(id)
         .orElseThrow(()-> new CustomerServiceException("No customer found for the given id - " + id));
    List<AccountDto> accounts = accountFeignClient.getAccountsByCustomerId(id);
    CustomerDto customerDto = CustomerDto.builder()
           .id(customer.getId())
           .name(customer.getName())
           .age(customer.getAge())
           .city(customer.getCity())
           .accounts(accounts)
           .build();
    return customerDto;
  }
}

As you can see AccountFeignClient is wired into CustomerService class and it is then used in getCustomerById() method to make a call to getAccountsByCustomerId() method.

accountFeignClient.getAccountsByCustomerId(id);

This call is then resolved to call the AccountController in the account-service as explained above.

Once these changes are done start the EurekaDiscoveryServiceApplication which starts the Eureka Server. Then start the CustomerService and AccountService applications.

Once all the services are started access http://localhost:8081/customer/1 where internally a call is made to account-service from customer-service using FeignClient.

Spring Boot Microservice Example Using FeignClient

That's all for this topic Spring Boot Microservice Example Using FeignClient. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot Microservice - Eureka + LoadBalancer + Feign
  2. Spring Boot Microservices Example
  3. Spring Boot REST API CRUD Example With Spring Data JPA
  4. Spring Boot Microservice Example Using WebClient
  5. Spring Boot Microservice + API Gateway + Resilience4J

You may also like-

  1. Spring Boot + Spring Security JWT Authentication Example
  2. Circular Dependency in Spring Framework
  3. Spring MVC Excel Generation Example
  4. Why Class Name And File Name Should be Same in Java
  5. Java Record Class With Examples
  6. Java Sealed Classes and Interfaces
  7. AtomicInteger in Java With Examples
  8. JavaScript Array and Object Destructuring

No comments:

Post a Comment