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-
- @EnableFeignClients- To be used with the application class. With this annotation applied framework scans for interfaces that declare they are feign clients.
- @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.
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.
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
You may also like-
No comments:
Post a Comment