Loading all records from a database table in a single query is impractical for large datasets – it consumes excessive memory, slows down the application, and degrades user experience. Spring Data JPA provides first‑class pagination and sorting support through the Pageable interface and Page return type – no manual SQL LIMIT/OFFSET needed. In this guide, we’ll walk through everything you need to know to implement efficient, production‑ready pagination in your Spring Boot applications.


The Key Classes

Spring Data JPA’s pagination API revolves around these core types:

Class / Interface Role
Pageable Input – describes the page number, page size, and sort order to fetch
PageRequest Concrete implementation of Pageable; used to construct page requests
Sort Describes sort direction and properties
Page<T> Output – contains the page data plus metadata (total elements, total pages, etc.)
Slice<T> Output – lighter than Page; contains data + hasNext flag, no total count query

Repository Setup

Extend JpaRepository (which already extends PagingAndSortingRepository) – pagination is inherited automatically:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface ProductRepository extends JpaRepository<Product, Long> {

    // findAll(Pageable) is inherited from PagingAndSortingRepository

    // Derived query method – also accepts Pageable
    Page<Product> findByCategory(String category, Pageable pageable);

    // Custom JPQL with pagination
    @Query("SELECT p FROM Product p WHERE p.price < :maxPrice")
    Page<Product> findAffordable(@Param("maxPrice") BigDecimal maxPrice, Pageable pageable);
}

Spring Data automatically generates the LIMIT/OFFSET SQL (or the equivalent for your database) and a count query for Page return types. You can also use @Query with native SQL if needed, but the JPQL version is database‑independent.


Creating a PageRequest

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

// Page 0 (first page), 10 results, no sort
Pageable pageable = PageRequest.of(0, 10);

// Page 2 (third page), 5 results, sorted by name ascending
Pageable sorted = PageRequest.of(2, 5, Sort.by("name"));

// Multiple sort fields
Pageable multiSort = PageRequest.of(0, 20,
    Sort.by(Sort.Direction.DESC, "createdAt")
        .and(Sort.by(Sort.Direction.ASC, "name")));

// Use the pageable to query the repository
Page<Product> page = productRepository.findAll(pageable);

Important: Page numbers are zero‑based: page 0 is the first page, page 1 is the second, and so on. This matches the convention used in Spring Data and most REST APIs.


What Page<T> Contains

Page<Product> page = productRepository.findAll(PageRequest.of(0, 10));

page.getContent();          // List<Product> – the actual data
page.getTotalElements();    // long – total records in database
page.getTotalPages();       // int – ceil(totalElements / pageSize)
page.getNumber();           // int – current page number (0-based)
page.getSize();             // int – page size requested
page.getNumberOfElements(); // int – actual elements in this page (may be less on last page)
page.isFirst();             // boolean
page.isLast();              // boolean
page.hasNext();             // boolean
page.hasPrevious();         // boolean

REST Controller with Pagination

Expose pagination as query parameters in your REST endpoint. Spring MVC can bind Pageable directly from request parameters when @EnableSpringDataWebSupport is active (enabled automatically with Spring Boot).

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    private final ProductRepository repository;

    public ProductController(ProductRepository repository) {
        this.repository = repository;
    }

    @GetMapping
    public Page<Product> getProducts(
        // @PageableDefault sets fallback values if parameters are not provided
        @PageableDefault(size = 20, sort = "name") Pageable pageable
    ) {
        return repository.findAll(pageable);
    }

    @GetMapping("/category/{category}")
    public Page<Product> getByCategory(
        @PathVariable String category,
        @PageableDefault(size = 10) Pageable pageable
    ) {
        return repository.findByCategory(category, pageable);
    }
}

The endpoint now accepts these query parameters:

  • page – page number (0‑based, default 0)
  • size – page size (default from @PageableDefault)
  • sort – sort field and direction, e.g. sort=name,desc

Example request: GET /api/products?page=1&size=10&sort=price,desc

You can also omit @PageableDefault and rely on global defaults by setting spring.data.web.pageable.default-page-size and spring.data.web.pageable.max-page-size in your application.properties.


JSON Response Structure

When you return Page<T> from a Spring MVC controller, it serializes to JSON with both content and metadata:

{
  "content": [
    { "id": 11, "name": "Keyboard", "price": 49.99 },
    { "id": 12, "name": "Mouse", "price": 29.99 }
  ],
  "pageable": {
    "sort": { "sorted": true, "unsorted": false },
    "pageNumber": 1,
    "pageSize": 10,
    "offset": 10
  },
  "totalPages": 5,
  "totalElements": 47,
  "last": false,
  "first": false,
  "numberOfElements": 10,
  "size": 10,
  "number": 1,
  "empty": false
}

This structure is compatible with most front‑end pagination components (e.g., Angular Material, React Table).


Page vs Slice

Spring Data JPA offers two main return types for paginated queries. Here’s how they compare:

Return Type Runs Count Query? Has Total Pages/Elements? Best for
Page<T> ✅ Yes ✅ Yes Traditional pagination with page numbers ("Page 3 of 12")
Slice<T> ❌ No (only checks if next page exists) ❌ No (only hasNext) Infinite scroll / "Load more" UIs; avoids expensive COUNT query
List<T> ❌ No ❌ No When you want data but handle pagination yourself (not recommended)

For large tables, the COUNT(*) query that Page requires can be expensive. If you're building an infinite scroll UI where you only need to know if there are more results, Slice is more efficient. To use Slice, simply change your repository method return type to Slice<T> – Spring Data will automatically generate the appropriate query without the count.


Sorting Without Pagination

You can also use Sort independently of Pageable when you need sorted results but don't need pagination:

// Repository method with Sort parameter
List<Product> findByCategory(String category, Sort sort);

// Usage:
Sort sort = Sort.by(Sort.Direction.ASC, "name");
List<Product> products = repository.findByCategory("Electronics", sort);

Performance Considerations

  • Large offsets: When you request a high page number (e.g., page 1000 with size 20), the generated SQL uses OFFSET 20000. This can be slow on large tables because the database still has to scan rows up to that offset. Consider using keyset pagination (also called “seek method”) if you need to navigate deeply. Spring Data doesn't support keyset pagination out‑of‑the‑box, but you can implement it with custom queries using WHERE id > :lastId.
  • Count queries: The COUNT(*) query for Page can become expensive on tables with millions of rows. If you have a very large table and don't need exact total counts, use Slice instead.
  • Sorting on unindexed columns: Sorting on columns without database indexes will force a full table scan. Always ensure that the columns you sort on are indexed, especially for large datasets.
  • Default page size: Set a reasonable default page size (e.g., 20 or 50) to avoid accidentally loading too many records at once. Use @PageableDefault or global properties to enforce a maximum size.

Sorting with Multiple Fields and Null Handling

You can control how null values are sorted using the Sort.Order class:

Sort sort = Sort.by(
    Sort.Order.asc("name").nullsFirst(),
    Sort.Order.desc("createdAt").nullsLast()
);
Pageable pageable = PageRequest.of(0, 10, sort);

This is useful when you want to place nulls at the beginning or end of the result set.


Using Pagination with Projections (DTOs)

If you want to fetch only a subset of columns (e.g., for performance), you can use DTO projections with pagination:

public interface ProductSummary {
    String getName();
    BigDecimal getPrice();
}

// Repository method
Page<ProductSummary> findByCategory(String category, Pageable pageable);

Spring Data will generate a query that selects only the needed columns, and the pagination works the same way as with entities.


Best Practices

  • Always set a maximum page size – use @PageableDefault with maxPageSize or configure spring.data.web.pageable.max-page-size to prevent clients from requesting huge pages that could overload your database.
  • Use Slice for infinite scroll – avoid the count query cost when you don’t need total page numbers.
  • Index your sort columns – ensure that all columns used in Sort.by() have database indexes.
  • Validate sort parameters – to prevent malicious sort fields, consider whitelisting allowed sort properties (e.g., by checking the field name against a set of known column names).
  • Prefer default values – always specify defaults for page, size, and sort so that your API works without any parameters.
  • Use @PageableDefault with sort and direction for a clean and consistent API.

Common Pitfalls and Troubleshooting

  • Page is 0‑based – many developers expect 1‑based pages. Make sure your front‑end sends page=0 for the first page, or you can create a custom Pageable resolver to convert from 1‑based.
  • Missing @EnableSpringDataWebSupport – in Spring Boot, it’s auto‑configured. In plain Spring MVC, you need to add it to a configuration class.
  • Sort parameter format – the query parameter should be like sort=name,desc (note: comma, not space). Multiple sorts: sort=name,desc&sort=id,asc.
  • Large offset performance – if you're experiencing slow queries on large offsets, consider implementing keyset pagination or using @Query with a native WHERE clause that uses the last retrieved ID.
  • Count query fails – when using complex joins or distinct queries, the count query may fail. You can provide a custom count query with @Query(countQuery = "...").

Summary

Spring Data JPA's pagination is built around three components: PageRequest.of(page, size, sort) to specify what you want, repository methods that accept Pageable and return Page<T>, and the Page object that carries both the data and metadata (total elements, total pages, current page). In a REST controller, declare Pageable as a method parameter and Spring MVC automatically binds it from page, size, and sort query parameters. Use Slice<T> instead of Page<T> when you want to avoid the count query on very large tables, and always protect your endpoints with sensible defaults and maximum page sizes.

With these tools, you can build efficient, user‑friendly APIs that handle large datasets gracefully.


Happy coding!

Most Spring Boot tutorials focus on building REST APIs. But in practice, most applications also need to consume them — calling third‑party services, microservices in your own stack, or internal APIs. Spring's RestTemplate is the traditional way to do this, and while Spring 5 introduced the reactive WebClient, RestTemplate is still widely used and the right choice for blocking, synchronous HTTP calls in servlet‑based applications.


Setup

No extra dependency needed for RestTemplate — it's part of spring-boot-starter-web. The typical setup uses RestTemplateBuilder, which Spring Boot auto‑configures and injects:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

The examples here also use Jackson's JsonNode to work with JSON responses generically, which is already included through the web starter.


Example 1: Fetching GitHub API Endpoints

GitHub's root API endpoint returns a map of all available API URLs. This makes a good first example because it returns structured JSON with no authentication required:

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Iterator;
import java.util.Map;

@Component
@Order(1)
public class GithubEndpointLoader implements CommandLineRunner {

    private final RestTemplate restTemplate;

    public GithubEndpointLoader(RestTemplateBuilder builder) {
        this.restTemplate = builder.build();
    }

    @Override
    public void run(String... args) {
        String url = "https://api.github.com";
        ResponseEntity<JsonNode> response =
            restTemplate.getForEntity(url, JsonNode.class);

        System.out.println("GitHub API Endpoints:");
        Iterator<Map.Entry<String, JsonNode>> fields = response.getBody().fields();
        while (fields.hasNext()) {
            Map.Entry<String, JsonNode> entry = fields.next();
            System.out.println("  " + entry.getKey() + " -> " + entry.getValue().asText());
        }
    }
}

getForEntity(url, responseType) makes a GET request and wraps the response in a ResponseEntity, giving you access to the status code, headers, and body. Using JsonNode.class as the response type tells Jackson to parse the JSON into a generic tree structure — useful when you don't want to create a specific class just to parse a response.


Example 2: Fetching User Repositories

A more realistic example — fetching a list of repositories for a specific GitHub user. The response is a JSON array, so you need to check before iterating:

@Component
@Order(2)
public class GithubRepoLoader implements CommandLineRunner {

    private final RestTemplate restTemplate;

    public GithubRepoLoader(RestTemplateBuilder builder) {
        this.restTemplate = builder.build();
    }

    @Override
    public void run(String... args) {
        String url = "https://api.github.com/users/bootng/repos";
        ResponseEntity<JsonNode> response =
            restTemplate.getForEntity(url, JsonNode.class);

        JsonNode body = response.getBody();
        System.out.println("Repositories:");

        if (body != null && body.isArray()) {
            for (JsonNode repo : body) {
                System.out.println("  " + repo.get("name").asText()
                    + " — " + repo.get("description").asText());
            }
        }
    }
}

The @Order annotations on both classes control which CommandLineRunner executes first. Order 1 runs before Order 2.


Deserializing into a POJO Instead

For responses with a known structure, mapping to a POJO is cleaner than working with JsonNode:

public class GithubRepo {
    private String name;
    private String description;
    private int stargazersCount;

    // getters and setters
}

// Use an array type as the response type
ResponseEntity<GithubRepo[]> response =
    restTemplate.getForEntity(url, GithubRepo[].class);

GithubRepo[] repos = response.getBody();
for (GithubRepo repo : repos) {
    System.out.println(repo.getName());
}

Jackson maps JSON field names to Java field names. If the JSON uses snake_case (stargazers_count) and your Java uses camelCase (stargazersCount), add @JsonProperty("stargazers_count") to the field or configure an object mapper with MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES.


POST, PUT, DELETE

RestTemplate supports all HTTP methods:

// POST — send a request body, receive a response body
ResponseEntity<Blog> created =
    restTemplate.postForEntity("/api/blogs", newBlog, Blog.class);

// PUT — update a resource
restTemplate.put("/api/blogs/{id}", updatedBlog, blogId);

// DELETE — remove a resource
restTemplate.delete("/api/blogs/{id}", blogId);

Error Handling

By default, RestTemplate throws HttpClientErrorException (4xx responses) and HttpServerErrorException (5xx responses). You can handle them explicitly:

try {
    ResponseEntity<Blog> response =
        restTemplate.getForEntity("/api/blogs/nonexistent", Blog.class);
} catch (HttpClientErrorException.NotFound e) {
    System.out.println("Blog not found: " + e.getStatusCode());
} catch (HttpServerErrorException e) {
    System.out.println("Server error: " + e.getStatusCode());
}

For more control, implement a custom ResponseErrorHandler and set it on the RestTemplate instance.


Running the Application

mvn clean install
mvn spring-boot:run

The console output shows the GitHub API endpoints (from the first runner) followed by the repository list (from the second runner), ordered by the @Order annotations.


RestTemplate vs WebClient

Spring 5 introduced WebClient as the modern, non‑blocking alternative. RestTemplate is still maintained but no longer actively developed with new features. For most existing Spring MVC applications, RestTemplate is perfectly fine. Switch to WebClient if you're building reactive applications with Spring WebFlux or need non‑blocking I/O for performance reasons.


Summary

Spring Boot's RestTemplate provides a straightforward API for making HTTP requests from Java. Inject RestTemplateBuilder, call build() to get an instance, then use methods like getForEntity() to make requests. The response comes back as a ResponseEntity containing status, headers, and body. Use JsonNode.class as the response type for flexible generic JSON parsing, or map to a POJO for known response structures. For sequenced startup operations, implement CommandLineRunner with @Order to control execution sequence.


Happy coding!

Without centralized exception handling, every controller method ends up with try‑catch blocks, and every new exception type requires updating every controller that might throw it. @ControllerAdvice solves this by letting you define exception handling logic once, in one place, and have it apply across your entire application. It's one of those Spring features that pays off quickly once you have more than a handful of endpoints.


The Problem Without @ControllerAdvice

Here's what controller code looks like when exception handling is inline:

@GetMapping("/blogs/{id}")
public ResponseEntity<BlogStory> getBlog(@PathVariable String id) {
    try {
        BlogStory blog = blogService.findById(id);
        return ResponseEntity.ok(blog);
    } catch (NotFoundException e) {
        return ResponseEntity.notFound().build();
    } catch (AppException e) {
        return ResponseEntity.internalServerError().build();
    }
}

@PostMapping("/blogs")
public ResponseEntity<BlogStory> createBlog(@RequestBody BlogStory blog) {
    try {
        BlogStory created = blogService.create(blog);
        return ResponseEntity.status(201).body(created);
    } catch (AppException e) {  // same handler copied again
        return ResponseEntity.internalServerError().build();
    }
}

Every method that can throw AppException needs the same catch block. Add a new exception type and you're touching every controller. This is the problem @ControllerAdvice was designed to eliminate.


Custom Exception Classes

First, define the application‑specific exceptions:

public class NotFoundException extends RuntimeException {
    public NotFoundException(String message) {
        super(message);
    }
}

public class AppException extends RuntimeException {
    public AppException(String message) {
        super(message);
    }

    public AppException(String message, Throwable cause) {
        super(message, cause);
    }
}

The Global Exception Handler

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NotFoundException.class)
    public ResponseEntity<String> handleNotFound(NotFoundException ex) {
        return ResponseEntity
            .status(HttpStatus.NOT_FOUND)
            .body(ex.getMessage());
    }

    @ExceptionHandler(AppException.class)
    public ResponseEntity<String> handleAppException(AppException ex) {
        return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("Something went wrong: " + ex.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneral(Exception ex) {
        return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("Unexpected error occurred");
    }
}

Each @ExceptionHandler method handles one (or more) exception types. When a controller throws NotFoundException, Spring intercepts it and routes it to handleNotFound() instead of propagating. The controller never sees the exception — it only throws it.


Clean Controllers After @ControllerAdvice

The same controller methods from before, now without any error handling code:

@RestController
@RequestMapping("/blogs")
public class BlogAPIController {

    private final BlogService blogService;

    public BlogAPIController(BlogService blogService) {
        this.blogService = blogService;
    }

    @GetMapping("/{id}")
    public BlogStory getBlog(@PathVariable String id) {
        return blogService.findById(id);  // throws NotFoundException — handled globally
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public BlogStory createBlog(@RequestBody BlogStory blog) {
        return blogService.create(blog);  // throws AppException — handled globally
    }
}

The controller is now purely about orchestration — take the request, call the service, return the result. Exception handling is a cross‑cutting concern that belongs elsewhere, and @ControllerAdvice is that elsewhere.


Returning Structured Error Responses

Returning a plain string as the error body is functional but not ideal for API clients. A structured error response is more useful:

public class ErrorResponse {
    private int status;
    private String message;
    private long timestamp;

    public ErrorResponse(int status, String message) {
        this.status = status;
        this.message = message;
        this.timestamp = System.currentTimeMillis();
    }

    // getters
}

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(NotFoundException ex) {
    ErrorResponse error = new ErrorResponse(404, ex.getMessage());
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}

The JSON response now looks like:

{
  "status": 404,
  "message": "Blog with id 'xyz' not found",
  "timestamp": 1718123456789
}

Scoping @ControllerAdvice

By default, @ControllerAdvice applies to all controllers in the application. You can narrow its scope:

// Only applies to controllers in this package
@ControllerAdvice("com.example.api")

// Only applies to controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)

// Only applies to these specific controller classes
@ControllerAdvice(assignableTypes = {BlogAPIController.class, UserController.class})

In most cases, a single application‑wide advice class is the right approach. Multiple scoped advice classes can be useful in modular applications where different modules have different error response formats.


@RestControllerAdvice

If all your handler methods return response bodies (as in a REST API), use @RestControllerAdvice instead of @ControllerAdvice. It's the equivalent of combining @ControllerAdvice with @ResponseBody — the same way @RestController combines @Controller with @ResponseBody:

@RestControllerAdvice
public class GlobalExceptionHandler {
    // no need to wrap returns in ResponseEntity if you're always returning bodies

    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorResponse handleNotFound(NotFoundException ex) {
        return new ErrorResponse(404, ex.getMessage());
    }
}

Summary

@ControllerAdvice is Spring's mechanism for defining global exception handling logic that applies across all controllers. Instead of writing try‑catch blocks in every controller method, you define @ExceptionHandler methods in a single @ControllerAdvice class. Spring intercepts exceptions thrown by any controller and routes them to the appropriate handler. The result is cleaner controllers focused on business logic, consistent error responses across the API, and a single place to update when you add new exception types. Use @RestControllerAdvice for REST APIs where all responses serialise to JSON.


Happy coding!

RequestBody and ResponseBody

Spring RequestBody and ResponseBody annotations are used in Spring controllers, where we want to bind web requests to method parameters (RequestBody) or method return value (ResponseBody) to Web response. In this article, we will cover some quick examples of using both these annotations. Although Spring supports different styles of writing controller and accessing request, response object, using RequestBody and ResponseBody helps writing code quickly as all the parameters are already available in the controller, and Spring takes care of serialization and deserialization. Although using ResponseBody is not required if we use @RestController annotation. More about @Restcontroller can be found here @RestController

Serialization

Data sent over HTTP to server resources (Like a REST Controller) needs to be converted from serialized version to Objects. We can have different types of REST controllers based on the payload they support. For instance, we can design the REST controller to accept/send XML, JSON, TEXT, or HTML payloads/response. The Controller then should be able to read those data acts on it and return the response. Spring uses HTTP Message converters to convert the HTTP request body into domain object [deserialize request body to domain object], and to convert Domain object back to HTTP response body while returning the response. Spring provides a number of MessageConvertes like bellow, StringHttpMessageConverter: Read Write String
FormHttpMessageConverter: Read Write HTTP Form Data
MappingJackson2HttpMessageConverter: Read Write JSON
MappingJackson2XmlHttpMessageConverter: Read Write XML etc.
@RequestBody and @ResponseBody body annotation behind the scene uses these Message converter to serialize or deserialize the data. Following section, we will see each example of these two annotations.

@RequestBody annotations

Typically in each controller, we can have multiple methods. Each method is tied to a specific HTTP request path via @RequestMapping. That's how Spring knows for an incoming request which method needs to be invoked. If a method parameter is annotated with @RequestBody, Spring will bind the incoming HTTP request body (payload) to the parameter of the respective method tied to the request path. While doing that, Spring will [behind the scenes] use HTTP Message converters to convert the HTTP request body into domain object [deserialize request body to domain object], based on Accept header present in the request.

@ResponseBody Annotation

Similar to the @RequestBody annotation the @ResponseBody annotation is used to convert the return type of the method to the HTTP response. Here also Spring will use Message Converter to convert the return type of the method to the HTTP response body, set the HTTP headers, and HTTP status.
Let's see an example, where we bind a JSON payload to a @RequestBody method parameter. Following the example, we will create a REST endpoint that can take a JSON payload to create a blog category object and then return the response back to the client.
Our BlogCategory Model
The model object representing a blog category.
public class BlogCategory {
  private String id;
  private String name;
  //Getters and Setters removed for readability
}
Sample JSON Payload
This JSON payload can be used to populate the BlogCategory object.
{
    "id": "new_cat",
    "name": "new cat neme"
}
A controller method to add a new category.
In the following example, we mark the method parameters "input" with @RequestBody. We also mark the return type of the method ResponseEntity with @ResponseBody.
@RequestMapping(value = {"/categories"}, method = RequestMethod.POST,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public @ResponseBody ResponseEntity addCats(@RequestBody BlogCategory input) {
    try {
      log.info("payload: " + input);
      blogService.addBlogCategories(input);
      return ResponseEntity.ok().body(input);
    } catch (Exception e) {
      log.info(e.getMessage());
      return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
  }
As a result, when the controller receives a request, the payload will be parsed and used to populate an instance of the BlogCategory object. Similarly when returning Web response back to client Spring will serialize the instance of and ResponseEntity to Web response.
Invoke API with Curl
We can invoke the API with payload by running the following curl command from a Terminal.
curl -X POST \
  http://localhost:8080/blogapi/categories \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "new_cat",
    "name": "new cat neme"
}'
API Response
The API should return with the following JSON payload.
{
    "id": "new_cat",
    "name": "new cat neme"
}
Summary
  • @RequestBody annotation facilitates deserialize the payload to a domain object.
  • @ResponseBody annotation facilitates serialization of the domain object to the payload.
  • @ResponseBody annotation is not required if we use @RestController rather then @Controller to annotate our Controller class.
  • @RestController is recommended over using @ResponseBody.
  • Another option is to use ResponseEntity as a return type of the controller method.
Git Source code
  • git clone https://github.com/siddharthagit/spring-boot-references
  • cd spring-rest
  • mvn clean install
  • mvn spring-boot:run

    References

    Spring ResponseEntity Example

    ResponseEntity class is used to represent HTTP response containing body, header and status. ResponseEntity class extends HttpEntity and used to encapsulate HTTP response. While writing the REST controller one of the common design questions is what should the controller method return? Also, we need to be cognizant about different REST endpoints and making sure all the responses maintain a standard pattern. All responses share common response building blocks like HTTP headers and HTTP Status. Using ResponseEntity is one of the options to write a REST controller, which helps in setting headers and status easily. ResponseEntity is not the only option we have while creating REST controllers. But It does provide some advantages In this short article, we will see the different ways of writing REST Controller and returning data from the controller. How to handing serialize the data from the controller depends on the use case we are dealing with. But we should keep in mind about easy maintenance of code and maintaining request-response structure across different endpoints. Using ResponseEntity is not only option to manipulate the response, but We can also return directly any Java object from the controller and let Spring do the serialization (Our Second example in this article). For setting the HTTP status only we can use @ResponseStatus annotation. For setting headers and status we can also javax.servlet.http.HttpServletResponse directly.

    ResponseEntity Class

    ResponseEntity Class is used to represent whole HTTP responses. It supports the the message body, headers, status, etc. Internally Response extends org.springframework.http.HttpEntity The advantage of using ResponseEntity is that it is easy to set headers status on this object directly which gets serialized as an HTTP response. Let's see an example controller using ResponseEntity

    Examples

    Example 1: Controller with ResponseEntity
    In this example, we are building a controller that returns a list of Tags as a response. Each tag is represented by a String object, so we are returning basically List We are wrapping a list of tags received form service in ResponseEntity> object. We can then set the HTTP status on the ResponseEntity object. If everything looks good, we are setting the status "OK 200", else we are setting HTTP status " 500 Internal Server Error".
    import java.util.List;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    import com.bootng.beans.AppException;
    
    @RestController
    @RequestMapping("/blogapi")
    public class TagAPIController {
    
    @RequestMapping(value = {"v1/tags"}, method = RequestMethod.GET,
          produces = MediaType.APPLICATION_JSON_VALUE)
      public ResponseEntity> getTagsV1() {
        try {
          List tags = blogService.getBlogTags();
          return ResponseEntity.ok().body(tags);
        } catch (AppException e) {
          log.error(e.getMessage());
          return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
      }
    }
    Example 2: Controller without ResponseEntity
    Following controller which returns the list of Tags as a JSON response. Notice that the method returns List and not ResponseEntity.
    import java.util.List;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    import com.bootng.beans.AppException;
    
    
    @RestController
    @RequestMapping("/blogapi")
    public class TagAPIController {
    
      private static final Logger log = LoggerFactory.getLogger(TagAPIController.class);
    
      @Autowired
      BlogService blogService;
      @RequestMapping(value = {"v3/tags"}, method = RequestMethod.GET,
          produces = MediaType.APPLICATION_JSON_VALUE)
      public List getTagsV3() {
        List tags = null;
        try {
          tags = blogService.getBlogTags();
        } catch (AppException e) {
          log.error(e.getMessage());
        }
        return tags;
      }
    }
    Response from both the above API / v3/tags and /v1/tags looks the same.
    Example 3
    In this example, we are writing the controller with HttpServletResponse as a parameter. This allows us to set the header and status directly on the object.
    @RequestMapping(value = {"/welcome"}, method = RequestMethod.GET,
          produces = MediaType.APPLICATION_JSON_VALUE)
    void welcome(HttpServletResponse response) throws IOException {
        response.setHeader("Custom-Header-Message-Type", "welcome_user");
    response.setHeader("Custom-Header-Message-LANG", "en-us");
        response.setStatus(200);
        response.getWriter().println("Welcome");
    }
    Following is a controller that also does the same stuff as the above controller. That is the return list of Tags. But here we are not using ResponseEntity, rather we are sending the Object directly from the controller method Let's see the example.
    Conclusions
    • We saw how to write a REST controller using ResponseEntity and without using ResponseEntity.
    • ResponseEntity makes it easy to set common HTTP Response elements like status code, headers, etc.
    • ResponseEntity is definitely useful but makes the overall controller method less readable the controller method return type is now ResponseEntity wrapped actual return type.

    References

    Spring Boot REST API example

    REST is an acronym for REpresentational State Transfer. In this article, we will walk through a sample REST application built using Spring Boot. This application does not use any database to store data, all the data is in memory. We will expose a few REST endpoints. Main purpose of this article is to demonstrate how to build REST API's using Spring boot.

    Application Details

    This application is about building a backend for a Blog software and exposing two REST API endpoints.
    Following two endpoints both returns response in JSON format.
    /blogapi/blogs : Returns list of blogs
    /blogapi/blogs/ID : Returns details of a specific blog
    Technology Used
    • Spring Boot 2.2.6.RELEASE
    • Logback 1.2.3
    • Maven 3
    • Java 11
    • Jackson 2.10.3

    Project Structure


    Code Reference

    Maven pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<artifactId>com.bootng.rest</artifactId>
    	<version>1.0.0-SNAPSHOT</version>
    	<packaging>war</packaging>
    	<name>Spring Boot Rest</name>
    	<description>Springboot Rest App</description>
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.2.6.RELEASE</version>
    		<relativePath />
    	</parent>
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    		<!-- junit 5 -->
    		<dependency>
    			<groupId>org.junit.jupiter</groupId>
    			<artifactId>junit-jupiter-engine</artifactId>
    			<scope>test</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.mockito</groupId>
    			<artifactId>mockito-core</artifactId>
    			<version>2.19.0</version>
    			<scope>test</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.junit.jupiter</groupId>
    			<artifactId>junit-jupiter-api</artifactId>
    		</dependency>
    	</dependencies>
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    				<configuration>
    					<addResources>true</addResources>
    				</configuration>
    				<executions>
    					<execution>
    						<goals>
    							<goal>repackage</goal>
    						</goals>
    					</execution>
    				</executions>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    BlogStory Model
    BlogStory model class which represent a blog entry.
    package com.bootng.model;
    
    public class BlogStory {
      private String id;
      private String name;
      private String summary;
      private String description;
      private String category;
      public BlogStory() {
      }
      public BlogStory (String name, String category, String summary) {
        this.id = name.replaceAll(" ","_");
        this.name = name;
        this.summary = summary;
        this.category = category;
        this.description = summary + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt "
            + "ut labore et dolore magna aliqua. "
            + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
            + " Duis aute irure dolor in reprehenderit in voluptate velit esse cillum "
            + "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
            + "sunt in culpa qui officia deserunt mollit anim id est laborum" ;   
      }
    // Setters
     
    }
    BlogService Class
    Service class which returns list of available blogs. Blog with an id etc.
    @Service
    public class BlogService {
      List category = Arrays.asList("Technical", "Travel", "Food", "Finance", "Entertainment");
      List stories = new ArrayList();
      {
        stories.add(new BlogStory("Java 11", "Technical", "Java 11 Blog"));
        stories.add(new BlogStory("Java 14", "Technical", "Java 14 Blog"));
        stories.add(new BlogStory("Asia Travel", "Travel", "Places to visit in Asia"));
        stories.add(new BlogStory("Europe Travel", "Travel", "Places to visit in Europe"));
        stories.add(new BlogStory("Japan Travel", "Travel", "Places to visit in Japan"));
        stories.add(new BlogStory("Asian Food", "Food", "Asian Food......"));
      }
    
      public BlogStory getBlogStory(String id) throws AppException {
        return stories.stream().filter(story -> id.equals(story.getId())).findAny().orElse(null);
      }
    
      public List getBlogStory() throws AppException {
        return stories;
      }
    
      public void addStory(BlogStory newStory) throws AppException {
        this.stories.add(newStory);
      }
    
      public List getBlogTags() throws AppException {
        return category;
      }
    }
    Main Application Class
    Spring Boot's main application class.
    @ComponentScan({"com.bootng"})
    @SpringBootApplication
    public class RestApplication {
      public static void main(String args[]) {
        SpringApplication.run(RestApplication.class, args);
      }
    }
    BlogAPIController Controller Class
    Controller class which exposes the two endpoints
    GET /blogapi/blogs
    and GET /blogapi/blogs/ID
    @Controller annotation is used to mark this class as a Controller.
    @RequestMapping is used to map the request paths "/blogs" to getBlogSotries method and /blog/ID to getBlogStory method.
    In both cases we are mapping both the paths to HTTP GET verb by using method = RequestMethod.GET
    @ResponseBody is used to convert the result to JSON (as we specified by produces=MediaType.APPLICATION_JSON_VALUE)
    @Controller
    @RequestMapping("/blogapi")
    public class BlogAPIController {
    private static final Logger log = LoggerFactory.getLogger(BlogAPIController.class);
    @Autowired
    BlogService blogService;
    ResponseEntity apiResponse = new ResponseEntity(newStory, HttpStatus.OK);
        return apiResponse;
    }
    
    @RequestMapping(value = {"/blogs"}, method = RequestMethod.GET,
          produces = MediaType.APPLICATION_JSON_VALUE)
      public @ResponseBody ResponseEntity> getBlogStories() {
        log.info("inside getBlogStories GET method");
        List blogStory = null;
        ResponseEntity> apiResponse;
        try {
          blogStory = blogService.getBlogStory();
          apiResponse = new ResponseEntity>(blogStory, HttpStatus.OK);
        } catch (AppException e) {
          apiResponse =
              new ResponseEntity>(blogStory, HttpStatus.INTERNAL_SERVER_ERROR);
          e.printStackTrace();
        }
        return apiResponse;
      }
    
    @RequestMapping(value = {"/blogs"}, method = RequestMethod.GET,
          produces = MediaType.APPLICATION_JSON_VALUE)
      public @ResponseBody ResponseEntity getBlogStory(@PathParam(value = "") String id) {
        log.info("inside blog GET method");
        BlogStory blogStory = null;
        ResponseEntity apiResponse;
        try {
          blogStory = blogService.getBlogStory(id);
          if (blogStory == null)
            apiResponse = new ResponseEntity(blogStory, HttpStatus.NOT_FOUND);
          else
            apiResponse = new ResponseEntity(blogStory, HttpStatus.OK);
        } catch (AppException e) {
          apiResponse = new ResponseEntity(blogStory, HttpStatus.INTERNAL_SERVER_ERROR);
          e.printStackTrace();
        }
        return apiResponse;
      }
    }

    Start Application

    Run Application
    Build the project using mvn clean install and then run it with mvn spring-boot:run
    mvn clean install
    mvn spring-boot:run

    Call REST API's using CURL

    CURL: Get list of Blogs
    Using curl we can get list of blogs from the /blogs endpoint
    curl -X GET  http://localhost:8080/blogapi/blogs 
    CURL: Get specific blog details
    Get a specific blog with id "Java_14" by calling /blogs/Java_14
    curl -X GET localhost:8080/blogapi/blogs/Java_14
    Git Source Code
    • git clone https://github.com/siddharthagit/spring-boot-references
    • cd springboot-rest
    • mvn clean install
    • mvn spring-boot:run

      Conclusion

      In this article, we used Springs RestController annotation to build the API. With Spring boot we can use other frameworks like Jersey, Restlet, etc also to build API.

      Spring RestController vs Controller

      Spring 4 introduced a new annotation called RestController. It makes writing REST controllers a little easy as we don't have to repeat the @ResponseBody annotations in each and every controller method. In this short article, we will examine how to write the REST API controller using both the annotations. While writing the REST controller it is recommended to use @RestController rather then @Controller.

      @Controller Example

      Controller annotation is a classic annotation which marks a Class as a controller and helps Spring to autodetect these Classes though Class scanning. While developing REST API in Spring Boot, we can mark a Class as Controller. We then need to use the @ResponseBody annotation to be used so that whatever the method returns can be serialized.
      In this article, we will go through two examples a simple Controller which returns a list of categories and tags as JSON response respectively using both these annotations.

      Let's see an example with Controller annotation.
      CategoryController controller contains a method "getBlogCats" that returns a list of categories, it is mapped to the request path "/blogapi/categories". Here we are using @Controller annotation to mark this class as Controller. We need to use @ResponseBody annotation in the method getBlogCats so that List can be serialized and sent back to the client as Web response.
      CategoryController Class
      @Controller
      @RequestMapping("/blogapi")
      public class CategoryController {
      
        private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
      
        @Autowired
        BlogService blogService;
      
        @RequestMapping(value = {"/categories"}, method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
        public @ResponseBody ResponseEntity<List<String>> getBlogCats() {
          ResponseEntity<List<String>> apiResponse;
          try {
            List<String> categories = blogService.getBlogCategories();
            apiResponse = new ResponseEntity<List<String>>(categories, HttpStatus.OK);
          } catch (AppException e) {
            apiResponse = new ResponseEntity<List<String>>(HttpStatus.INTERNAL_SERVER_ERROR);
            log.error(e.getMessage());
          }
          return apiResponse;
        }
      }
      Next we will see a REST controller with @RestController. This Controller creates a GET REST endpoint that returns a list of Tags. It can be accessed as GET /blogapi/tags. The method getBlogTags() returns ResponseEntity>. Since we are marking the Class with @RestController, Spring will serialize the return type to HTTP response body and return to the client. Notice that we don't need to use @ResponseBody annotation.

      @RestController Example

      TagAPIController.Java
      @RestController
      @RequestMapping("/blogapi")
      public class TagAPIController {
        private static final Logger log = LoggerFactory.getLogger(TagAPIController.class);
        @Autowired
        BlogService blogService;
      
        @RequestMapping(value = {"/tags"}, method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
        public ResponseEntity getBlogTags() {
          ResponseEntity<List<String>> apiResponse;
          try {
            List<String> tags = blogService.getBlogTags();
            apiResponse = new ResponseEntity<List<String>>(tags, HttpStatus.OK);
          } catch (AppException e) {
            apiResponse = new ResponseEntity<List<String>>(HttpStatus.INTERNAL_SERVER_ERROR);
            log.error(e.getMessage());
          }
          return apiResponse;
        }
      }
      In this example since we marked the controller with @RestController , we don't need to mark getBlogTags with @ResponseBody explicitly.
      Summary
      • @RestController is available from Spring 4.0 onwards.
      • @RestController is a composed annotation containing both @Controller and @ResponseBody annotations.
      • While writing REST controller it is better to use @RestController.
      • In this article, we have seen two different Controllers one using @Controller and another using @RestController.

      References

      Spring Boot Custom Banner

      Spring Boot applications display startup information in the console, usually starting with a banner:
        .   ____          _            __ _ _
       /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
      ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
       \/  ___)| |_)| | | | | || (_| |  ) ) ) )
        '  |____| .__|_| |_|_| |_\__, | / / / /
       =========|_|==============|___/=/_/_/_/
       :: Spring Boot ::        (v2.2.6.RELEASE)
          

      How to Turn Banner Off

      The banner can be turned off via configuration or Java code.
      application.properties
      Set the following property to turn off the banner:
      spring.main.banner-mode=off
      Main Application Class
      @SpringBootApplication
      public class HelloApplication implements CommandLineRunner {
          private static final Logger log = LoggerFactory.getLogger(HelloApplication.class);
      
          public static void main(String[] args) {
              var app = new SpringApplication(HelloApplication.class);
              app.setBannerMode(Banner.Mode.OFF);
              app.run(args);
          }
      
          @Autowired
          HelloService service;
      
          @Override
          public void run(String... args) throws Exception {
              log.info(service.hello());
          }
      }
            

      Changing the Banner

      Provide ASCII banner content in a file and set its location via `spring.banner.location` in `application.properties`. You can include additional properties like `application.title` and `application.version` in the banner.

      Code Details

      my-banner.txt
      ASCII banner using Spring Boot's `AnsiColor.RED` for coloring.
      ${AnsiColor.RED}
       _                    _                 
      | |                  | |                
      | |__    ___    ___  | |_  _ __    __ _ 
      | '_ \  / _ \  / _ \ | __|| '_ \  / _` |
      | |_) || (_) || (_) || |_ | | | || (_| |
      |_.__/  \___/  \___/  \__||_| |_| \__, |
                                         __/ |
                                        |___/ 
      Application Name: ${application.title}
      Application Version: ${application.version}  
      Spring Boot Version: ${spring-boot.version}
      ${AnsiColor.DEFAULT}
      application.properties
      #spring.main.banner-mode=off
      spring.banner.location=classpath:my-banner.txt
      application.title=Spring Boot Standalone App
      application.version=1.0.0

      Run The Application

      mvn spring-boot:run
      Console Output
       _                    _
      | |                  | |
      | |__    ___    ___  | |_  _ __    __ _
      | '_ \  / _ \  / _ \ | __|| '_ \  / _` |
      | |_) || (_) || (_) || |_ | | | || (_| |
      |_.__/  \___/  \___/  \__||_| |_| \__, |
                                         __/ |
                                        |___/
      
      Application Name: Spring Boot Standalone App
      Application Version:
      Spring Boot Version: 2.2.6.RELEASE
            

      Git Source Code

      • git clone https://github.com/siddharthagit/spring-boot-references
      • cd spring-boot-standalone-app
      • mvn clean

      How to create Stand alone app?

      With Spring Boot we can also create a stand-alone application, which does not need any web server or application server to run. In this article, we will go through a very simple Spring boot stand-alone application using the CommandLineRunner interface.
      We will go through a very simple Spring Boot application trying to understand how CommandLineRunner works and what are the alternatives to CommandLineRunner interface like Application Runner. CommandLineRunner : This interface can be used to dictate that a class that implements this interface should be executed once the SpringApplication context loaded. This interface has a single method "run" which takes String array as arguments. ApplicationRunner : Similar to CommandLineRunner how it works, it also has only one method "run", which gets executed once the SpringBoot Context loads. The only difference between these two interfaces is argument types. the run method in ApplicationRunner accepts an array of ApplicationArguments whereas run method in CommandLineRunner accepts array or Strings.
      CommandLineRunner.java
      Definition of CommandLineRunner interface. Note that it is a Functional interface, meaning it has only one method.
      @FunctionalInterface
      public interface CommandLineRunner {
      	/**
      	 * Callback used to run the bean.
      	 * @param args incoming main method arguments
      	 * @throws Exception on error
      	 */
      	void run(String... args) throws Exception;
      }

      Technology Used

      • Java 11
      • Apache Maven 3.5.0
      • Spring Boot 2.2.6
      • Logback 1.2.3
      • Eclipse IDE

      Code Structure


      Application Details

      pom.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
      	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      	<modelVersion>4.0.0</modelVersion>
      	<artifactId>bootng-springboot-hello</artifactId>
      	<version>1.0.0</version>
      	<packaging>jar</packaging>
      	<name>Spring Boot Standalone App</name>
      	<description>Spring Boot Hello Standalone Application</description>
      	<parent>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-parent</artifactId>
      		<version>2.2.6.RELEASE</version>
      		<relativePath />
      	</parent>
      	<properties>
      		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      		<java.version>1.8</java.version>
      	</properties>
      	<dependencies>
      	<dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter</artifactId>
            </dependency>
      	<dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-test</artifactId>
               <scope>test</scope>
            </dependency>
      	</dependencies>
      	<build>
      		<plugins>
      			<plugin>
      				<groupId>org.springframework.boot</groupId>
      				<artifactId>spring-boot-maven-plugin</artifactId>
      				<configuration>
      					<addResources>true</addResources>
      				</configuration>
      			</plugin>
      		</plugins>
      	</build>
      </project>
      HelloService
      Our service Class defines three methods hello, bye, and onDestroy marked with @PreDestroy annotation.
      package com.bootng;
      import javax.annotation.PreDestroy;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.stereotype.Service;
      
      @Service
      public class HelloService {
        private static final Logger log = LoggerFactory.getLogger(HelloService.class);
        public String hello() {
          return "Hello Spring Boot!";
        }
        @PreDestroy
        public void onDestroy() throws Exception {
          log.info("Spring Container is destroyed!");
          bye();
        }
        public String bye() {
          String msg = "Bye..... Have a nice day!";
          log.info(msg);
          return msg;
        }
      }
      
      HelloApplication Class
      Our HelloApplication Class implements CommandLineRunner interface. We inject the HelloService using @Autowired annotation. In the run method, we call the service.hello method.
      package com.bootng;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.CommandLineRunner;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.context.annotation.ComponentScan;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      @ComponentScan({"com.bootng"})
      @SpringBootApplication
      public class HelloApplication implements CommandLineRunner {
        private static final Logger log = LoggerFactory.getLogger(HelloApplication.class);
        @Autowired
        HelloService service;
        @Override
        public void run(String... args) throws Exception {
          log.info(service.hello());
        }
      }

      Running the application

      Run the application by executing the following command
      mvn spring-boot:run
      Console Output
      20-June-01 16:28:57:549 INFO main c.b.HelloApplication:651 - No active profile set, falling back to default profiles: default 20-June-01 16:28:57:844 INFO main c.b.HelloApplication:61 - Started HelloApplication in 0.64 seconds (JVM running for 0.872) 20-June-01 16:28:57:845 INFO main c.b.HelloApplication:26 - Hello Spring Boot! 20-June-01 16:28:57:846 INFO SpringContextShutdownHook c.b.HelloService:21 - Spring Container is destroyed! 20-June-01 16:28:57:847 INFO SpringContextShutdownHook c.b.HelloService:27 - Bye..... Have a nice day!
      We can see the application will execute and terminate with messages like "Hello Spring Boot!" and Bye..."Have a nice day!"
      First message is printed by run->service.hello() method as soon as the application starts.
      The second message is somewhat special and is printed by invoking service.bye() by the Spring framework before the application exits.
      Git Source Code
      • git clone https://github.com/siddharthagit/spring-boot-references
      • cd spring-boot-standalone-app
      • mvn clean install
      • mvn spring-boot:run

        Summary

        Using CommandLineRunner we can create a stand alone Spring Boot application
        We can use the @PreDestroy to call methods from service class before it is unloaded by Spring while exiting the application



        Testing Spring Boot App with JUnit 5 and Mockito

        Writing Unit Tests for an application is very important to make sure the software works as expected. JUnit 5 is the latest release of Junit 5 and it is already gaining popularity. In this article, we will walk through a simple Spring Boot Application and will write unit tests using Junit and Mockito.

        Purpose

        Before we dig into would like to give a broad overview of JUnit 5 and Mockito. Junit5 is the latest release of Junit, it is much different then Junit4, so many of the Junit4 specific annotations do not work with Junit 5. Mockito is a mocking framework and is very popular in the Opensource community. With Mockito we can mock an object or a method. For example in this article, we will be writing unit testing for the controller and we will mock the service layer.

        Application Details

        The Spring boot application we are building here is a simple App with one controller and one service class. The application creates rest endpoints (/blogs, /blogs/ID) through which we can get the list of blogs and specific blog details.

        Code Structure

        Spring Boot JUNIT5 Project Structure


        Code details

        pom.xml
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
         <modelVersion>4.0.0</modelVersion>
         <artifactId>com.bootng.springboot-rest</artifactId>
         <version>1.0.0-SNAPSHOT</version>
         <packaging>war</packaging>
         <name>bootngSpringboot  Rest</name>
         <description>bootng Springboot Rest</description>
         <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.2.6.RELEASE</version>
          <relativePath />
         </parent>
         <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
          <java.version>1.8</java.version>
         </properties>
         <dependencies>
          <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
          <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
          <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-test</artifactId>
           <scope>test</scope>
          </dependency>
          <!-- junit 5 -->
          <dependency>
           <groupId>org.junit.jupiter</groupId>
           <artifactId>junit-jupiter-engine</artifactId>
           <scope>test</scope>
          </dependency>
          <dependency>
           <groupId>org.mockito</groupId>
           <artifactId>mockito-core</artifactId>
           <version>2.19.0</version>
           <scope>test</scope>
          </dependency>
          <dependency>
           <groupId>org.junit.jupiter</groupId>
           <artifactId>junit-jupiter-api</artifactId>
          </dependency>
         </dependencies>
         <build>
          <plugins>
           <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
             <addResources>true</addResources>
            </configuration>
            <executions>
             <execution>
              <goals>
               <goal>repackage</goal>
              </goals>
             </execution>
            </executions>
           </plugin>
          </plugins>
         </build>
        </project>
        
        RestApplication.java
        Our main application class.
        package com.bootng;
        
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        import org.springframework.context.annotation.ComponentScan;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        
        @ComponentScan({"com.bootng"})
        @SpringBootApplication
        public class RestApplication {
          private static final Logger log = LoggerFactory.getLogger(RestApplication.class);
          public static void main(String args[]) {
            log.info("about to call RestApplication.run()");
            SpringApplication.run(RestApplication.class, args);
            log.info("completed executing RestApplication.run()");
          }
        }
        
        BlogService.java
        Our service class which has three methods getBlogStory(String id), getBlogStory() and getBlogTags() respectively. Controller class which exposes the REST endpoints will call these methods.
        @Service
        public class BlogService {
          List category = Arrays.asList("Technical", "Travel", "Food", "Finance", "Entertainment");
          List stories = new ArrayList();
          {
            stories.add(new BlogStory("Java 11", "Technical", "Java 11 Blog"));
            stories.add(new BlogStory("Java 14", "Technical", "Java 14 Blog"));
            stories.add(new BlogStory("Asia Travel", "Travel", "Places to visit in Asia"));
            stories.add(new BlogStory("Europe Travel", "Travel", "Places to visit in Europe"));
            stories.add(new BlogStory("Japan Travel", "Travel", "Places to visit in Japan"));
            stories.add(new BlogStory("Asian Food", "Food", "Asian Food......"));
          }
        
          public BlogStory getBlogStory(String id) throws AppException{
            return stories.stream().filter(story -> id.equals(story.getId())).findAny().orElse(null);
          }
          public List getBlogStory() throws AppException {
            return stories;
          }
          public List getBlogTags() throws AppException{
            return category;
          }
        }
        BlogAPIController.java
        Controller class which creates two rest endpoints /blogs and /blog/ID. Both methods might return an error responses.
        @Controller
        @RequestMapping("/blogapi")
        public class BlogAPIController {
          private static final Logger log = LoggerFactory.getLogger(BlogAPIController.class);
          @Autowired
          BlogService blogService;
        
          @RequestMapping(value = {"/blogs"}, method = RequestMethod.GET,
              produces = MediaType.APPLICATION_JSON_VALUE)
          public @ResponseBody ResponseEntity> getBlogStories() {
            log.info("inside getBlogStories GET method");
            List blogStory = null;
            ResponseEntity> apiResponse;
            try {
              blogStory = blogService.getBlogStory();
              apiResponse = new ResponseEntity>(blogStory, HttpStatus.OK);
            } catch (AppException e) {
              apiResponse = new ResponseEntity>(blogStory, HttpStatus.INTERNAL_SERVER_ERROR);
              e.printStackTrace();
            }
            return apiResponse;
          }
          
          @RequestMapping(value = {"/blog"}, method = RequestMethod.GET,
              produces = MediaType.APPLICATION_JSON_VALUE)
          public @ResponseBody ResponseEntity  getBlogStory(@PathParam(value = "") String id) {
            log.info("inside blog GET method");
            BlogStory blogStory = null;
            ResponseEntity  apiResponse;
            try {
              blogStory = blogService.getBlogStory(id);
              if (blogStory == null) 
                apiResponse = new ResponseEntity(blogStory, HttpStatus.NOT_FOUND);
              else
                apiResponse = new ResponseEntity(blogStory, HttpStatus.OK);
            } catch (AppException e) {
              apiResponse = new ResponseEntity(blogStory, HttpStatus.INTERNAL_SERVER_ERROR);
              e.printStackTrace();
            }
             return apiResponse;
          }
        }
        BlogServiceTest.java
        In this Test class, we are testing the two methods of the Service class. We get an autowired instance of the BlogService class. We use the @ExtendWith annotation of JUnit5 . We also use the @ContextConfiguration annotation from Spring Boot to load the appropriate context.
        @ExtendWith(SpringExtension.class)
        @ContextConfiguration(classes = {BlogService.class})
        public class BlogServiceTest {
        
          @Autowired
          BlogService service;
        
          @Test
          public void test_getBlogStory() {
            List list;
            try {
              list = service.getBlogStory();
              Assertions.assertNotNull(list, "list should not be null");
            } catch (AppException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
            }
          }
        
          @Test
          public void test_getBlogStory_with_id() {
            BlogStory data;
            try {
              data = service.getBlogStory("Java_11");
              Assertions.assertNotNull(data, "data should not be null");
            } catch (AppException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
            }
          }
        }
        @ExtendWith(SpringExtension.class)
        @ContextConfiguration(classes = {BlogAPIController.class, BlogService.class})
        @TestInstance(TestInstance.Lifecycle.PER_CLASS)
        public class BlogAPIControllerTest {
          
          @Mock
          BlogService blogService;
          
          @InjectMocks
          private BlogAPIController controller;
          
          @BeforeAll
          public void setup() {
              MockitoAnnotations.initMocks(this);
          }
          
          @Test
          public void contextLoads() {}
          
          @Test
          public void test_getBlogStories() throws AppException {
            
            when(blogService.getBlogStory()).thenReturn(new ArrayList());
            
            ResponseEntity> object = controller.getBlogStories();
            
            Assertions.assertEquals(HttpStatus.OK, object.getStatusCode(), "OK Status");
            
          }
          
          @Test
          public void test_getBlogStory_not_found() throws AppException {
            
            when(blogService.getBlogStory("444")).thenReturn(null);
            
            ResponseEntity object = controller.getBlogStory("444");
            
            Assertions.assertEquals(HttpStatus.NOT_FOUND, object.getStatusCode(), "OK Status");
            
          }
        
        }
        BlogAPIControllerTest
        Our controller test class. In this class we use Mokito to inject a mocked BlogService, then we can use the stubbing method like "when" to return different results from the service class.
        @ExtendWith(SpringExtension.class)
        @ContextConfiguration(classes = {BlogAPIController.class, BlogService.class})
        @TestInstance(TestInstance.Lifecycle.PER_CLASS)
        public class BlogAPIControllerTest {
          
          @Mock
          BlogService blogService;
          
          @InjectMocks
          private BlogAPIController controller;
          
          @BeforeAll
          public void setup() {
              MockitoAnnotations.initMocks(this);
          }
         
          @Test
          public void contextLoads() {}
          
          @Test
          public void test_getBlogStories_success() throws AppException {
            
            when(blogService.getBlogStory()).thenReturn(new ArrayList());
            
            ResponseEntity> object = controller.getBlogStories();
            
            Assertions.assertEquals(HttpStatus.OK, object.getStatusCode(), "OK Status");
         }
          
          @Test
          public void test_getBlogStory_Not_found() throws AppException {
            
            when(blogService.getBlogStory("444")).thenReturn(null);
            
            ResponseEntity object = controller.getBlogStory("444");
            
            Assertions.assertEquals(HttpStatus.NOT_FOUND, object.getStatusCode(), "OK Status");
          }
        
        }

        Summary

        What is covered
        In this article, we saw how to write a simple Spring Boot Application. Write Unit tests and execute unit tests. Mock service classes.
        Source code and Build
        git clone https://github.com/bootng/spring-boot-references cd springboot-junit #build the application mvn install #run the tests mvn test