Monday, September 25, 2023

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

Wednesday, September 20, 2023

Spring Boot Microservice Example Using WebClient

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 WebClient for communication between microservices.

As per Spring framework documentation:
As of Spring 5.0 org.springframework.web.client.RestTemplate class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.

So, this is one of the reasons for considering using WebClient.

WebClient

Spring WebFlux includes WebClient to call remote REST services. WebClient has a functional, fluent API based on Reactor. It is fully non-blocking and it supports streaming.

Spring Boot Microservice with WebClient 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 WebClient in place of RestTemplate.

Add maven dependency

To use WebClient Spring Webflux is needed so you need to add Webflux starter 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.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Configuration for WebClient

We'll configure WebClient.Builder instance which in turn is used to create a WebClient instance, where ever it is needed using build() method.

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {
	
	@Bean
	@LoadBalanced
	WebClient.Builder loadBalancedWebClientBuilder(){
		return WebClient.builder();
	}
}

Changes in CustomerService implementation

import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
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;
import reactor.core.publisher.Flux;


@Service
public class CustomerServiceWebClientImpl implements CustomerService{
  
  private WebClient.Builder webClientBuilder;
  private CustomerRepository customerRepository;
  
  CustomerServiceWebClientImpl(WebClient.Builder webClientBuilder, CustomerRepository customerRepository){
    this.webClientBuilder = webClientBuilder;
    this.customerRepository = customerRepository;
  }
  
  
  private String baseURL = "http://ACCOUNT-SERVICE/account/";
  
  @Override
  public Customer saveCustomer(Customer customer) {
    return customerRepository.save(customer);
  }

  @Override
  public CustomerDto getCustomerById(Long id) {
    Customer customer = customerRepository.findById(id)
         .orElseThrow(()-> new CustomerServiceException("No customer found for the given id - " + id));
    Flux<AccountDto> accountFlux = getWebClient(id);
    System.out.println("Flux " + accountFlux);
    List<AccountDto> accounts = accountFlux.collectList().block();
    System.out.println(accounts);
    // Need to create CustomerDTO so need accounts data
    CustomerDto customerDto = CustomerDto.builder()
           .id(customer.getId())
           .name(customer.getName())
           .age(customer.getAge())
           .city(customer.getCity())
           .accounts(accounts)
           .build();
    return customerDto;
  }
  
  Flux<AccountDto> getWebClient(Long customerId){
    return webClientBuilder.baseUrl(baseURL)
                 .build()
                 .get()
                 .uri("customer/" + customerId)
                 .accept(MediaType.APPLICATION_JSON)
                 .retrieve()
                 .bodyToFlux(AccountDto.class);
                     
  }
}

For sending request and receiving response there are two methods provided by WebClient- exchange() and retrieve(). Out of these two, exchange() method is deprecated since Spring 5.3. In place of that there are two alternative methods exchangeToFlux() and exchangeToMono(). These methods provide more control via access to the ClientResponse. This can be useful for advanced scenarios, for example to decode the response differently depending on the response status.

With retrieve() method you can declare how to extract the response. For example, you can extract a ResponseEntity with status, headers, and body or you can just retrieve the body. In the usage as shown above in the CustomerServiceWebClientImpl only body is retrieved.

After retrieve() method bodyToFlux() or bodyToMono() method is used to decode the body to a Flux with elements of the given type or to decode the body to the given target type. In the example shown here we need a collection of accounts so Flux is used.

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 WebClient.

Spring Boot Microservice Example Using WebClient

That's all for this topic Spring Boot Microservice Example Using WebClient. 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 Microservices Introduction
  2. Spring Boot Microservices Example
  3. Spring Boot Microservice Example Using FeignClient
  4. Spring Boot Microservice - Externalized Configuration With Spring Cloud Config
  5. Spring Boot Microservice Circuit Breaker Using Resilience4j

You may also like-

  1. Spring depends-on Attribute and @DependsOn With Examples
  2. How to Inject Null And Empty String Values in Spring
  3. Spring NamedParameterJdbcTemplate Insert, Update And Delete Example
  4. Add Double Quotes to a String Java Program
  5. StringBuffer Class in Java With Examples
  6. Namespace And Variable Scope in Python
  7. Custom Async Validator in Angular Template-Driven Form
  8. Controlled and Uncontrolled Components in React

Tuesday, September 19, 2023

super Keyword in Java With Examples

The super keyword in java is essentially a reference variable that can be used to refer to class' immediate parent class.

Usage of super in Java

super keyword in Java can be used for the following-

  • Invoke the constructor of the super class. See example.
  • Accessing the variables and methods of parent class. See example.

First let's see how code will look like if super() is not used.

Let's say there is a super class Shape with instance variables length and breadth. Another class Cuboids extends it and add another variable height to it. When you are not using super(), constructors of these 2 classes will look like-

public class Shape {
 int length;
 int breadth;
 Shape(){
  
 }
 // Constructor
 Shape(int length, int breadth){
  this.length = length;
  this.breadth = breadth;
 }
}

class Cuboids extends Shape{
 int height;
 //Constructor
 Cuboids(int length, int breadth, int height){
  this.length = length;
  this.breadth = breadth;
  this.height = height;
 }
}

It can be noticed there are 2 problems with this approach-

  • Duplication of code as the same initialization code in the constructor is used twice. Once in Cuboids class and once in Shape class.
  • Second and most important problem is super class instance variables can not be marked as private because they have to be accessed in child class, thus violating the OOP principle of Encapsulation.

So super() comes to the rescue and it can be used by a child class to refer to its immediate super class. Let's see how super keyword can be used in Java.

Using super to invoke the constructor of the super class

If you want to initialize the variables that are residing in the immediate parent class then you can call the constructor of the parent class from the constructor of the subclass using super() in Java.

Note: If you are using super to call the constructor of the parent class then super() should be the first statement inside the subclass' constructor. This ensures that if you call any methods on the parent class in your constructor, the parent class has already been set up correctly.

Java example code using super

public class Shape {
 private int length;
 private int breadth;
 Shape(){
  
 }
 Shape(int length, int breadth){
  this.length = length;
  this.breadth = breadth;
 }
}

class Cuboids extends Shape{
 private int height;
 Cuboids(int length, int breadth, int height){
  // Calling super class constructor
  super(length, breadth);
  this.height = height;
 }
}

Here it can be noticed that the instance variables are private now and super() is used to initialize the variables residing in the super class thus avoiding duplication of code and ensuring proper encapsulation.

Note: If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the super class does not have a no-argument constructor, you will get a compile-time error.

That will happen when a constructor is explicitly defined for a class. Then the Java compiler will not insert the default no-argument constructor into the class. You can see it by making a slight change in the above example.

In the above example, if I comment the default constructor and also comment the super statement then the code will look like-

public class Shape {
  private int length;
  private int breadth;
  /*Shape(){
   
  }*/
  Shape(int length, int breadth){
   this.length = length;
   this.breadth = breadth;
  }
}

class Cuboids extends Shape{
  private int height;
  Cuboids(int length, int breadth, int height){
   // Calling super class constructor
   /*super(length, breadth);*/
   this.height = height;
  }
}

This code will give compile-time error. "Implicit super constructor Shape() is undefined".

Using super to access Super class Members

If method in a child class overrides one of its superclass' methods, method of the super class can be invoked through the use of the keyword super. super can also be used to refer to a hidden field, that is if there is a variable of the same name in the super class and the child class then super can be used to refer to the super class variable.

Java example showing use of super to access field

public class Car {
 int speed = 100;
 
}

class FastCar extends Car{
 int speed = 200;
 FastCar(int a , int b){
  super.speed = 100;
  speed = b;
 }
}

Here, in the constructor of the FastCar class super.speed is used to access the instance variable of the same name in the super class.

Example showing use of super to access parent class method

public class Car {
 void displayMsg(){
  System.out.println("In Parent class Car");
 }
}

class FastCar extends Car{
 void displayMsg(){
  System.out.println("In child class FastCar");
  // calling super class method
  super.displayMsg();
 }
 public static void main(String[] args){
  FastCar fc = new FastCar();
  fc.displayMsg();
 }
}
public class Test {

 public static void main(String[] args){
  FastCar fc = new FastCar();
  fc.displayMsg();
 }
}

Output

In child class FastCar
In Parent class Car

Points to note-

  • super keyword in java is a reference variable to refer to class' immediate parent class.
  • super can be used to invoke the constructor of the immediate parent class, that's help in avoiding duplication of code, also helps in preserving the encapsulation.
  • If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass.
  • super can also be used to access super class members.
  • If a variable in child class is shadowing a super class variable, super can be used to access super class variable. Same way if a parent class method is overridden by the child class method then the parent class method can be called using super.

That's all for this topic super Keyword in Java With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Basics Tutorial Page


Related Topics

  1. final Keyword in Java With Examples
  2. this Keyword in Java With Examples
  3. static Keyword in Java With Examples
  4. TypeWrapper Classes in Java
  5. Core Java Basics Interview Questions And Answers

You may also like-

  1. Method Overloading in Java
  2. Constructor Chaining in Java
  3. Inheritance in Java
  4. Covariant Return Type in Java
  5. How to Read Input From Console in Java
  6. Difference Between Checked And Unchecked Exceptions in Java
  7. Deadlock in Java Multi-Threading
  8. Java ThreadLocal Class With Examples