You are here
Home > java >

Spring Cloud Annotations With Examples

Spring Cloud Annotations With ExamplesIntroduction of new Annotations reduces the development efforts day by day. Needless to say, as a developer, we can’t think of the development of an enterprise level application without using annotations, especially in applications that use Spring or related frameworks. Furthermore, we come across the Spring Cloud framework when we develop a Microservices based application. Now-a-days, there is a high demand of Microservices based applications in the industry. Therefore, it becomes very crucial to know the annotations used in Spring Cloud. Hence, in this article, we are going to discuss ‘Spring Cloud Annotations With Examples’.

We can’t deny from the fact that the cloud is the future and, in the upcoming days, we will be seeing a lot of Java based applications deployed in the cloud. So, it’s better to learn and master the Spring Cloud. In the future it might become the standard framework to develop cloud-based Java applications. Let’s start discussing about our topic ‘Spring Cloud Annotations With Examples’ and the related concepts.

Spring Cloud Annotations With Examples

Spring Cloud provides a set of annotations that simplify the development of microservices by addressing common challenges in microservices based applications. These annotations help us to manage configurations, handle service discovery, implement load balancing, and more. Let’s explore some key Spring Cloud annotations with examples one by one:

@EnableEurekaServer

When you develop a Microservices based project using Spring Boot & Spring Cloud, this will be the first annotation that you will apply to the main class of a microservice to make it a Eureka Server. As the name suggests, we want to enable Eureka Server. We will use this annotation in the context of Service Registry & Discovery. In the concept of Service Registry, every microservices registers itself with Eureka server. On the other hand, Service Discovery is the concept where one microservice discovers other microservice with the help of its entry in the Eureka server. In order to learn more about Eureka, kindly visit Netflix Eureka Service Registry & Discovery.

Moreover, on using Spring Cloud’s annotation @EnableEurekaServer, other microservices can register here and communicate with each other via service discovery. For example, in order to make your application/microservice acts as Eureka server, we need to apply @EnableEurekaServer at the main class of your application as shown below.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class SpringCloudEurekaServerApplication {

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

@EnableEurekaClient

This annotation relates to the concept of Service Discovery in Microservices. Using Service Discovery, one microservice can communicate with the other microservice via Eureka Server. Hence, other microservices who wants to register with Eureka Server & get discovered with the help of Eureka Server, become the candidate for this annotation.

In order to make your application/microservice acts as a Eureka discovery client, you need to apply @EnableEurekaClient at the main class of your application as shown below.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@SpringBootApplication
public class SpringCloudPaymentServiceApplication {

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

@EnableDiscoveryClient

@EnableDiscoveryClient is a more generic implementation of “Discovery Service” which comes from spring-cloud-commons.

@EnableEurekaClient works only with Eureka, whereas @EnableDiscoveryClient works with multiple implementations of “Discovery Service” such as eurekaconsulzookeeper. But if Eureka is on the application classpath, they are effectively the identical.

@EnableFeignClients

In Microservices, communication among multiple services happens on the concept of producer-consumer. Moreover, the Consumer service will consume the services published by producer service. At the consumer side we apply this annotation. Feign is a declarative REST Client. It makes writing web service clients easier.

In order to get the features of OpenFeign, we will additionally need to apply @EnableFeignClients at the main class as below.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SpringCloudFeignStudentServiceApplication {

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

@FeignClient(name=”ApplicationName”)

This annotation comes with the annotation @EnableFeignClients. At Consumer’s Interface level, we need to apply @FeignClient annotation and provide the name of producer service/application. For example, below code snippet demonstrate the concept.

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.dev.springcloud.feign.model.Book;

@FeignClient(name="BOOK-SERVICE")
public interface BookRestConsumer {

      @GetMapping("/book/data")
      public String getBookData();

      @GetMapping("/book/{id}")
      public Book getBookById(@PathVariable Integer id);

      @GetMapping("/book/all")
      public List<Book> getAllBooks();

      @GetMapping("/book/entity")
      public ResponseEntity<String> getEntityData();
}

Furthermore, in order to receive the data from Book service, we need to auto-wire the BookRestConsumer where it is required. Additionally, for more details on Feign Client related concepts, kindly visit a separate article on ‘How To Implement Feign Client In Spring Boot Microservices?‘.

@LoadBalanced

This annotation is used with RestTemplate to enable client-side load balancing. It ensures that requests to a service name (e.g., "myService") are distributed across instances. The RestTemplate bean will be auto-configured by Spring Cloud. In summary, we need to use the @LoadBalanced Annotation in our configuration class while creating the Bean of RestTemplate as shown in the example below.

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class MyServiceApplication {

   @Bean
   @LoadBalanced
   public RestTemplate restTemplate() {
      return new RestTemplate();
   }

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

@EnableConfigServer

Config Server is a central configuration server that provides configurations (properties) to each microservice connected to it. In order to make a Microservice acts as Config Server, we need to apply @EnableConfigServer annotation at the main class of the microservice. For example, below code snippet demonstrates the use of @EnableConfigServer annotation.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableConfigServer
public class SpringCloudConfigServerApplication {

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

Furthermore, Spring Cloud Config Clients are the microservices that utilize the services provided by Spring Cloud Config Server. You may visit a separate detailed article on ‘How To Implement Spring Cloud Config Server In Microservices?‘ and the related concepts.

@RefreshScope

The @RefreshScope annotation is used in Spring Cloud Config to allow dynamic reloading of configuration properties without restarting the application.

import org.springframework.cloud.context.config.annotation.RefreshScope;

@RestController
@RefreshScope
public class MyController {

   @Value("${property}")
   private String property;

   @GetMapping("/property")
   public String getProperty() {
      return property;
   }
}

@StreamListener

This annotation is part of Spring Cloud Stream and is used for message-driven microservices. It allows you to consume messages from message brokers like Apache Kafka or RabbitMQ.

import org.springframework.cloud.stream.annotation.StreamListener;

@EnableBinding(MySink.class)
public class MyListener {
   
   @StreamListener(MySink.INPUT)
   public void handle(String message) {
       // Process the message
   }
}

Annotations On Fault Tolerance provided by Resilience4j

Fault tolerance means being able to handle and deal with something going wrong or not working properly. In a context of Microservices, Fault Tolerance is a technique of tolerating a fault. A Microservice that tolerates the fault is known as Fault Tolerant. Moreover, a Microservice should be a fault tolerant in such a way that the entire application runs smoothly. There are possibilities of various kinds of faults after running a microservices based application. In order to implement this technique, the Resilience4j offers us a variety of modules based on the type of fault we want to tolerate.

Here, we will be discussing the annotations used for different types of faults. For complete details on Resilience4j API, kindly visit our separate article on ‘How To Implement Fault Tolerance In Microservices Using Resilience4j?’.

@RateLimiter

Rate Limiter limits the number of requests for a given period. There are various reasons to limit the number of requests that an API can handle, such as protect the resources from spammers, minimize the overhead, meet a service level agreement and many others. We can achieve this functionality with the help of annotation @RateLimiter provided by Resilience4j without writing a code explicitly. For example, below code snippet demonstrates the functionality of @RateLimiter applied on a method.

@GetMapping("/getMessage")
@RateLimiter(name = "getMessageRateLimit", fallbackMethod = "getMessageFallBack")
public ResponseEntity<String> getMessage(@RequestParam(value="name", defaultValue = "Hello") String name){

    return ResponseEntity.ok().body("Message from getMessage() :" +name);
}

@Retry 

Suppose Microservice ‘A’  depends on another Microservice ‘B’. Let’s assume Microservice ‘B’ is a faulty service and its success rate is only upto 50-60%. However, fault may be due to any reason, such as service is unavailable, buggy service that sometimes responds and sometimes not, or an intermittent network failure etc. However, in this case, if Microservice ‘A’ retries to send request 2 to 3 times, the chances of getting response increases. We can achieve this functionality with the help of annotation @Retry provided by Resilience4j without writing a code explicitly. For example, below code snippet demonstrates the functionality of @RateLimiter applied on a method.

@GetMapping("/getInvoice")
@Retry(name = "getInvoiceRetry", fallbackMethod = "getInvoiceFallback") 
public String getInvoice() {
   logger.info("getInvoice() call starts here");
   ResponseEntity<String> entity= restTemplate.getForEntity("http://localhost:8080/invoice/rest/find/2", String.class);
   logger.info("Response :" + entity.getStatusCode());
   return entity.getBody();
}

@CircuitBreaker

As the name suggests, ‘Breaking the Circuit’. Suppose a Microservice ‘A’ is internally calling another Microservice ‘B’ and ‘B’ has some fault. Needless to say, in Microservice Architecture ‘A’ might be dependent on other Microservices and the same is true for Microservice ‘B’. In order to escape the multiple microservices from becoming erroneous as a result of cascading effect, we stop calling the faulty Microservice ‘B’. Instead, we call a dummy method that is called a ‘Fallback Method’. Therefore, calling a fallback method instead of an actual service due to a fault is called breaking the circuit.

Using Circuit Breaker we can eliminate the flow of failures to downstream/upstream. We can achieve this functionality easily with the help of annotation @CircuitBreaker without writing a specific code. For example, below code demonstrates the concept of @CircuitBreaker that is applied on a method.

@GetMapping("/getInvoice")
@CircuitBreaker(name = "getInvoiceCB", fallbackMethod = "getInvoiceFallback") 
public String getInvoice() { 
   logger.info("getInvoice() call starts here");
   ResponseEntity<String> entity= restTemplate.getForEntity("http://localhost:8080/invoice/rest/find/2", String.class);
   logger.info("Response :" + entity.getStatusCode());
   return entity.getBody();
}

@Bulkhead

In the context of the Fault Tolerance mechanism, if we want to limit the number of concurrent requests, we can use Bulkhead as an aspect. Using Bulkhead, we can limit the number of concurrent requests within a particular period. We can achieve this functionality easily with the help of annotation @Bulkhead without writing a specific code. For example, below code snippet demonstrates the usage of annotation @Bulkhead that is applied on a method.

@GetMapping("/getMessage")
@Bulkhead(name = "getMessageBH", fallbackMethod = "getMessageFallBack")
public ResponseEntity<String> getMessage(@RequestParam(value="name", defaultValue = "Hello") String name){

    return ResponseEntity.ok().body("Message from getMessage() :" +name);
}

@Timelimiter

Time Limiting is the process of setting a time limit for a Microservice to respond. Suppose that Microservice ‘A’ sends a request to Microservice ‘B’, it sets a time limit for the Microservice ‘B’ to respond. If  Microservice ‘B’ doesn’t respond within that time limit, then it will be considered that it has some fault. We can achieve this functionality easily with the help of annotation @Timelimiter without writing a specific code. For example, below code demonstrates the usage of @Timelimiter.

@GetMapping("/getMessageTL")
@TimeLimiter(name = "getMessageTL")
public CompletableFuture<String> getMessage() {
   return CompletableFuture.supplyAsync(this::getResponse);
}

For complete examples of all Fault Tolerance annotations, kindly visit the separate article on ‘How To Implement Fault Tolerance In Microservices Using Resilience4j?’.

These are the mostly used annotations in Spring Cloud/Microservices. If any other annotation is required to include in the future, the article will be updated accordingly.

FAQ

What is the difference between @EnableEurekaClient and @EnableDiscoveryClient?

Both annotations @EnableEurekaClient and @EnableDiscoveryClient are used in Spring Cloud to enable service registration and discovery within a microservices architecture, but they are not the same.

@EnableEurekaClient is a particular annotation aimed for applications that use Netflix Eureka as the service registry for service registration and discovery. When we use this annotation, our microservice is explicitly saying that it is an Eureka client and intends to register itself with a Eureka server for service discovery.

@EnableDiscoveryClient is a more generic annotation that works with a variety of service discovery solutions, not just Eureka. It is part of the larger Spring Cloud ecosystem and can be used with other service registries like Eureka, Consul or ZooKeeper. When we use this annotation, we make our application discoverable to a broader range of service discovery solutions.

Are Spring Cloud annotations limited to Java applications?

No, Spring Cloud annotations can be used in a variety of languages and frameworks, as long as the application is part of the Spring Cloud ecosystem. For example, some Spring Cloud components can interact with non-Java applications through standard protocols like HTTP, but the direct use of Spring Cloud annotations in non-Java applications may not be as straightforward. However, Spring Cloud annotations are most commonly used in Java applications.

 

 

 

 

Leave a Reply


Top