Spring Boot Tutorials Part #2: Spring Boot Features

{getToc} $title={Table of Contents}

Spring Boot Tutorials Part #2: Spring Boot Features


Spring Boot Features:

Spring Boot makes creating and running Spring-based applications simple and fast. Some of the key features are:


- Auto-Configuration: Spring Boot automatically sets up the beans and settings required based on the dependencies and properties in your project.

For example, if you have Spring MVC on your classpath Spring Boot sets up a web application with embedded Tomcat server SpringTemplateEngine and other web-related beans. You can also change or turn off the auto-configuration using annotations or properties.

- Dependency Management: Spring Boot offers a set of starter dependencies that contain the common libraries and frameworks for different types of applications.

For example, spring-boot-starter-web contains Spring MVC Tomcat Jackson and other web-related dependencies. You can also remove or modify the starter dependencies using Maven or Gradle.

- Starters: Spring Boot offers a number of starter projects that help you quickly start a new application with the dependencies and configuration you need. You can use Spring Initializr or Spring Tool Suite (STS) to generate a starter project based on your preferences and needs.

For example, you can select the web data security actuator and other starters for your project. 

- Actuator: Spring Boot offers a set of endpoints that show information about the running application such as health metrics mappings beans env etc. You can access these endpoints using HTTP or JMX. You can also customize or extend the actuator endpoints using annotations or properties.

- Testing: Spring Boot makes testing your application easy using various tools and frameworks such as JUnit TestNG Mockito etc.

You can use annotations like

@SpringBootTest
@WebMvcTest
@DataJpaTest etc.

To test different parts and layers of your application. You can also use TestRestTemplate or WebTestClient to test your web endpoints.

Let’s see how to use these features in our example.


Auto-configuration

To show the auto-configuration feature of Spring Boot let’s make a simple web application that returns a greeting message.

First, we need to add the spring-boot-starter-web dependency to our pom.xml file:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

</dependency>


Next, we need to create a controller class that handles the HTTP requests:


package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class GreetingController {

    @GetMapping("/")

    public String hello() {

        return "Hello, Spring Boot!";

    }

}


Finally, we need to create an application class that runs our application:


package com.example.demo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class DemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(DemoApplication.class, args);

    }

}


We use the @SpringBootApplication annotation on our application class. This annotation makes Spring Boot do a lot of things for us automatically. It imports the @EnableAutoConfiguration annotation, which tells Spring Boot to configure everything based on our dependencies and properties. It also imports the @ComponentScan and @EnableConfigurationProperties annotations, which enable Spring Boot to scan for components and load configuration properties.

Now we can run our application and open the http://localhost:8080/ URL in our browser. We should see a message that says “Hello, Spring Boot!”.

That’s all we need to do! We have built a web application with Spring Boot without writing any XML configuration or setting up any web server. Spring Boot has taken care of everything for us.


Adding more features

Let’s make our web application more interesting by adding some more features. Let’s say we want to use Thymeleaf as our template engine and H2 as our in-memory database.

First, we need to add two more dependencies to our pom.xml file: spring-boot-starter-thymeleaf and spring-boot-starter-data-jpa. These dependencies will bring in Thymeleaf and H2 along with other useful libraries.

<dependency>

    <groupId>org.springframework.boot</groupId>

  <artifactId>spring-boot-starter-thymeleaf</artifactId>

</dependency>

<dependency>

    <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>


Next, we need to create an entity class that represents a user in our database:


package com.example.demo;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;

@Entity

public class User {

    @Id

    @GeneratedValue(strategy = GenerationType.AUTO)

    private Long id;

    private String name;

    private String email;

    // standard constructors / setters / getters / toString

}


Next, we need to create a repository interface that extends JpaRepository and provides CRUD methods for the User entity:


package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.stereotype.Repository;

@Repository

public interface UserRepository extends JpaRepository<User, Long> {

}


Finally, we need to create a view template that displays a list of users in a table. We will use Thymeleaf as our template engine and place the template file under src/main/resources/templates/users.html:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <meta charset="UTF-8">

    <title>Users</title>

</head>

<body>

<h1>Users</h1>

<table>

    <thead>

    <tr>

        <th>ID</th>

        <th>Name</th>

        <th>Email</th>

    </tr>

    </thead>

    <tbody>

    <tr th:each="user : ${users}">

        <td th:text="${user.id}"></td>

        <td th:text="${user.name}"></td>

        <td th:text="${user.email}"></td>

    </tr>

    </tbody>

</table>

</body>

</html>


We use the Thymeleaf namespace and attributes to connect the data from the model to the view. We also use the ${users} expression to get the list of users that we will send from the controller.

Now we can run our application and open the http://localhost:8080/users URL in our browser. We should see a table with some users that are already created by Spring Boot using H2 database.

That’s all we need to do! We have added some more features to our web application with Spring Boot without writing any SQL queries or configuration files. Spring Boot has taken care of everything for us based on our pom.xml file and properties.


Starters:

Let’s make a new project using Spring Initializr. Spring Initializr is a web tool that helps you create a starter project with the dependencies and configuration that you want.


First, we need to go to the Spring Initializr website and enter some basic information about our project, such as group, artifact, name, description, etc.

Next, we need to pick the dependencies that we want to use in our project. For example, let’s say we want to make a web application with RESTful services, security, actuator, and H2 database. We can choose the following starters from the list:

• Spring Web 
• Spring Security 
• Spring Boot Actuator 
• Spring Data JPA 
• H2 Database


Finally, we need to click on the Generate button and download the zip file that has our starter project. We can then open the project in our IDE and start writing code.

Notice that our pom.xml file has only the starter dependencies that we have picked. We don’t need to mention the versions or manage the transitive dependencies of these starters. Spring Boot will do that for us.

Also notice that our application.properties file has some default properties for our application, such as server port, logging level, security username and password, etc. We can modify these properties or add more properties as per our needs.

Now we can run our application and open some of the endpoints that are given by the starters. 

For example:

http://localhost:8080/ - The default home page of our web application 
http://localhost:8080/actuator - The base path of the actuator endpoints that show information about our application 
http://localhost:8080/h2-console - The web console of H2 database where we can run SQL queries.

That’s all we need to do! We have made a new project with Spring Boot using Spring Initializr without writing any boilerplate code or configuration files. Spring Boot has given us a ready-to-use starter project with the dependencies and configuration that we want.


Using Actuator

Let’s use the same project that we have made using Spring Initializr.


First, we need to add the spring-boot-starter-actuator dependency to our pom.xml file:

<dependency>

    <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-actuator</artifactId>

</dependency>


Next, we need to turn on the actuator endpoints that we want to show. By default, only the health and info endpoints are available over HTTP. We can use the management.endpoints.web.exposure.include property to give a list of endpoints that we want to show. 

For example, let’s show all the endpoints:

management.endpoints.web.exposure.include=* 

We can also use the management.endpoints.web.exposure.exclude property to specify a list of endpoints that we want to hide. For example, let’s hide the env endpoint:

management.endpoints.web.exposure.exclude=env


Finally, we need to access the actuator endpoints using HTTP or JMX. The base path for the HTTP endpoints is /actuator by default. We can change it using the

 management.endpoints.web.base-path property. 

For example, let’s change it to /manage:

management.endpoints.web.base-path=/manage


Now we can run our application and open some of the actuator endpoints using curl or a browser. 

For example:

http://localhost:8080/manage/health - The health endpoint that tells the status of the application and its dependencies.

http://localhost:8080/manage/metrics - The metrics endpoint that tells various metrics about the application such as memory usage request count and so on.

http://localhost:8080/manage/beans - The beans endpoint that tells the beans and their dependencies in the application context. We can also open some of the actuator endpoints using JMX. 

For example, we can use jconsole or jvisualvm to connect to our application and look at the org.springframework.boot domain. We should see a list of MBeans that match the actuator endpoints.

That’s all we need to do! We have added some production-ready features to our application with Spring Boot without writing any code or configuration files. Spring Boot has given us a set of endpoints that show information about our application and its environment.


Testing:

We can write unit tests for our application using the JUnit framework and the Mockito library. We can also use the spring-boot-starter-test dependency that has both of them along with other useful libraries. And can use some annotations such as @SpringBootTest, @WebMvcTest, @DataJpaTest, etc. to easily test different layers and components of your application. You can also use TestRestTemplate or WebTestClient to test your web endpoints.

Unit tests are tests that check the functionality of a single class or method. They are usually fast and isolated from other components or dependencies. Let’s see some examples of how to write unit tests for our application using Spring Boot.

First, we need to add the spring-boot-starter-test dependency to our pom.xml file:

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

Next, we need to create a test class that uses the @RunWith and @SpringBootTest annotations to enable the Spring Boot features in our tests:

package com.example.demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @Test
    public void contextLoads() {
    }
}

The @RunWith annotation tells JUnit to use the SpringRunner class as the test runner. The SpringRunner class is another name for the SpringJUnit4ClassRunner class that provides integration between Spring Boot and JUnit.

The @SpringBootTest annotation tells Spring Boot to make an application context with all the beans and configuration that we want for our tests. We can also give some properties or classes for our application context using this annotation.

Now we can write some unit tests for our controller class using the @MockBean and @Autowired annotations:

package com.example.demo;
import static org.hamcrest.Matchers.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@WebMvcTest(GreetingController.class)
public class GreetingControllerTest {
    @Autowired
    private MockMvc mvc;

    @MockBean
    private GreetingService service;

    @Test
    public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
      throws Exception {
        Greeting alex = new Greeting("Hello, Alex!");
        List<Greeting> allGreetings = Arrays.asList(alex);           given(service.getAllGreetings()).willReturn(allGreetings);
          mvc.perform(get("/greetings").contentType(MediaType.APPLICATION_JSON))
          .andExpect(status().isOk())
              .andExpect(jsonPath("$", hasSize(1)))           .andExpect(jsonPath("$[0].content", is(alex.getContent())));
    }
}


The @MockBean annotation tells Spring Boot to make a mock object for the GreetingService class and put it in the application context. The mock object will replace any existing bean of the same type in the application context.

The @Autowired annotation tells Spring Boot to inject the mock object into our test class. We can then use the mock object to set the behavior of the service methods using Mockito.

The MockMvc class is a helper class that lets us send HTTP requests and check the responses. We can use it to test our web layer without actually starting a server.

We can then write some unit tests for our service class using the @DataJpaTest and @TestEntityManager annotations:

Test class that uses the @DataJpaTest annotation to test the persistence layer:

package com.example.demo;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private EmployeeRepository repository;

    @Test
    public void whenFindByName_thenReturnEmployee() {
        // given
        Employee bob = new Employee("Bob", "bob@example.com");
        entityManager.persistAndFlush(bob);

        // when
        Employee found = repository.findByName(bob.getName());
        // then
        assertThat(found.getName()).isEqualTo(bob.getName());
    }

    @Test
    public void whenInvalidName_thenReturnNull() {

        // when
        Employee fromDb = repository.findByName("doesNotExist");

        // then
        assertThat(fromDb).isNull();
    }

    @Test
    public void whenFindAll_thenReturnAllEmployees() {

        // given
        Employee bob = new Employee("Bob", "bob@example.com");     Employee alice = new Employee("Alice", "alice@example.com");
        entityManager.persist(bob);
        entityManager.persist(alice);
        entityManager.flush();

        // when
        List<Employee> allEmployees = repository.findAll();
        // then
       assertThat(allEmployees).hasSize(2).extracting(Employee::getName)     .containsOnly(bob.getName(), alice.getName());
    }

}

The @DataJpaTest annotation tells Spring Boot to make a test context with only the beans needed for testing JPA repositories. It also sets up an in-memory database and sets Hibernate to create-drop mode.

The TestEntityManager class is a helper class that lets us persist and find entities using JPA. We can use it to set up the data for our tests.

The EmployeeRepository interface extends JpaRepository and has some methods for querying employees. We can use it to check the results of our tests.

Testing the web layer

We can write web tests for our application using the MockMvc or WebTestClient classes. We can also use the @WebMvcTest or @WebFluxTest annotations to test only the web layer of our application.

First, we need to add the spring-boot-starter-web dependency to our pom.xml file:

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

Next, we need to create a test class that uses the @WebMvcTest annotation to test our controller class using MockMvc:

package com.example.demo;
import static org.hamcrest.Matchers.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@WebMvcTest(GreetingController.class)
public class GreetingControllerTest {
    @Autowired
    private MockMvc mvc;

    @MockBean
    private GreetingService service;

    @Test
    public void givenGreetings_whenGetGreetings_thenReturnJsonArray()

      throws Exception {
        Greeting alex = new Greeting("Hello, Alex!");
        List<Greeting> allGreetings = Arrays.asList(alex);           given(service.getAllGreetings()).willReturn(allGreetings);
            mvc.perform(get("/greetings")
              .contentType(MediaType.APPLICATION_JSON))
              .andExpect(status().isOk())
              .andExpect(jsonPath("$", hasSize(1)))
          .andExpect(jsonPath("$[0].content", is(alex.getContent())));
    }
}

The @WebMvcTest annotation tells Spring Boot to create a test context with only the beans required for testing the web layer. It also configures MockMvc for us.



MockMvc:

The MockMvc class is a helper class that lets us send HTTP requests and check the responses. We can use it to test our web layer without actually starting a server.

We can use the @Autowired annotation to inject a MockMvc instance into our test class. We can also use the @MockBean annotation to make and inject mock objects for the services or repositories that are used by our controller.

We can use the MockMvcRequestBuilders class to make requests for different HTTP methods and URIs. We can also give request parameters headers content type and body.

We can use the MockMvcResultMatchers class to assert the results of the requests. We can check the status code content type headers and body of the response. We can also use JSONPath expressions to check specific parts of the JSON response.

We can use the MockMvcResultHandlers class to print or log the results of the requests. This can be helpful for debugging or troubleshooting purposes.



Using WebTestClient:

We can write web tests for our application using WebFlux using the WebTestClient class. We can also use the @WebFluxTest annotation to test only the web layer of our reactive application.

First, we need to add the spring-boot-starter-webflux dependency to our pom.xml file:

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

Next, we need to create a test class that uses the @WebFluxTest annotation to test our reactive controller class using WebTestClient:

package com.example.demo;
import static org.mockito.BDDMockito.given;
import static org.springframework.web.reactive.function.BodyInserters.fromValue; import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
@WebFluxTest(GreetingController.class)
public class GreetingControllerTest {
    @Autowired
    private WebTestClient webClient;

    @MockBean
    private GreetingService service;

    @Test
    public void givenGreetings_whenGetGreetings_thenReturnJsonArray() {
        // given
        Greeting alex = new Greeting("Hello, Alex!");
        List<Greeting> allGreetings = Arrays.asList(alex);
    given(service.getAllGreetings()).willReturn(allGreetings);

        // when/then
        webClient.get().uri("/greetings")
    .accept(MediaType.APPLICATION_JSON)
          .exchange()
          .expectStatus().isOk()
          .expectBodyList(Greeting.class)         .contains(alex);
    }
    @Test
    public void givenNewGreeting_whenPostGreeting_thenCreateGreeting() {
        // given
        Greeting bob = new Greeting("Hello, Bob!");     given(service.createGreeting(bob)).willReturn(bob);
        // when/then
        webClient.post().uri("/greetings")            
    .contentType(MediaType.APPLICATION_JSON)
          .body(fromValue(bob))
          .exchange()
          .expectStatus().isCreated()       .expectBody(Greeting.class)
         .isEqualTo(bob);

    }
}

The @WebFluxTest annotation tells Spring Boot to make a test context with only the beans needed for testing the web layer. It also sets up WebTestClient for us.

The WebTestClient class is a reactive web client that lets us send HTTP requests and check the responses. We can use it to test our web layer without actually starting a server.

We can use the @Autowired annotation to inject a WebTestClient instance into our test class. We can also use the @MockBean annotation to make and inject mock objects for the services or repositories that are used by our controller.

We can use the WebTestClient methods to make requests for different HTTP methods and URIs. We can also give request parameters headers content type and body.

We can use the WebTestClient.ResponseSpec methods to assert the results of the requests. We can check the status code content type headers and body of the response. We can also use JSONPath expressions or custom matchers to check specific parts of the JSON response.


Conclusion:

The ends of our section on Spring Boot Features. We have learned how to use some of the main features of Spring Boot such as auto-configuration dependency management starters actuator and testing. We have also seen some examples of how to make and run Spring Boot applications with these features.




Previous Post Next Post