About this book

Microservices helps in decomposing applications into small services and move away from a single monolithic artifact. It helps in building systems that are scalable, flexible, and high resilient. Spring Boot helps in building REST-oriented, production-grade microservices.

This book is a quick learning guide on how to build, monitor, and deploy microservices with Spring Boot. You'll be first familiarized with Spring Boot before delving into building microservices. You will learn how to document your microservice with the help of Spring REST docs and Swagger documentation. You will then learn how to secure your microservice with Spring Security and OAuth2. You will deploy your app using a self-contained HTTP server and also learn to monitor a microservice with the help of Spring Boot actuator.

This book is ideal for Java developers who knows the basics of Spring programming and want to build microservices with Spring Boot.

This book is embedded with useful assessments that will help you revise the concepts you have learned in this book.

Publication date:
March 2018
Publisher
Packt
Pages
140
ISBN
9781789132588

 

Chapter 1. Building Microservices with Spring Boot

As we discussed in the last lesson, we are moving toward architectures with smaller, independently deployable microservices. This would mean that there will be a huge number of smaller microservices developed.

An important consequence is that we would need to be able to quickly get off the ground and get running with new components.

Spring Boot aims to solve the problem of getting off fast with a new component. In this lesson, we will start with understanding the capabilities Spring Boot brings to the table. We will answer the following questions:

  • Why Spring Boot?

  • What are the features that Spring Boot provides?

  • What is auto-configuration?

  • What is Spring Boot not?

  • What happens in the background when you use Spring Boot?

  • How do you use Spring Initializr to create new Spring Boot projects?

  • How do you create basic RESTful services with Spring Boot?

 

What is Spring Boot?


First of all, let's start with clearing out a few misconceptions about Spring Boot:

  • Spring Boot is not a code generation framework. It does not generate any code.

  • Spring Boot is neither an application server, nor is it a web server. It provides good integration with different ranges of applications and web servers.

  • Spring Boot does not implement any specific frameworks or specifications.

These questions still remain:

  • What is Spring Boot?

  • Why has it become so popular in the last couple of years?

To answer these questions, let's build a quick example. Let's consider an example application that you want to quickly prototype.

Building a Quick Prototype for a Microservice

Let's say we want to build a microservice with Spring MVC and use JPA (with Hibernate as the implementation) to connect to the database.

Let's consider the steps in setting up such an application:

  1. Decide which versions of Spring MVC, JPA and Hibernate to use.

  2. Set up a Spring context to wire all the different layers together.

  3. Set up a web layer with Spring MVC (including Spring MVC configuration):

    • Configure beans for DispatcherServlet, handler, resolvers, view resolvers, and so on

  4. Set up Hibernate in the data layer:

    • Configure beans for SessionFactory, data source, and so on

  5. Decide and implement how to store your application configuration, which varies between different environments.

  6. Decide how you would want to do your unit testing.

  7. Decide and implement your transaction management strategy.

  8. Decide and implement how to implement security.

  9. Set up your logging framework.

  10. Decide and implement how you want to monitor your application in production.

  11. Decide and implement a metrics management system to provide statistics about the application.

  12. Decide and implement how to deploy your application to a web or application server.

At least a few of the steps mentioned have to be completed before we can start with building our business logic. And this might take a few weeks at the least.

When we build microservices, we would want to make a quick start. All the preceding steps will not make it easy to develop a microservice. And that's the problem Spring Boot aims to solve.

The following quote is an extract from the Spring Boot website (http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-documentation):

Spring Boot makes it easy to create stand-alone, production-grade Spring based applications that you can "just run". We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration

Spring Boot enables developers to focus on the business logic behind their microservice. It aims to take care of all the nitty-gritty technical details involved in developing microservices.

Primary Goals

The primary goals of Spring Boot are as follows:

  • Enable quickly getting off the ground with Spring-based projects.

  • Be opinionated. Make default assumptions based on common usage. Provide configuration options to handle deviations from defaults.

  • Provide a wide range of nonfunctional features out of the box.

  • Do not use code generation and avoid using a lot of XML configuration.

Nonfunctional Features

A few of the nonfunctional features provided by Spring Boot are as follows:

  • Default handling of versioning and configuration of a wide range of frameworks, servers, and specifications

  • Default options for application security

  • Default application metrics with possibilities to extend

  • Basic application monitoring using health checks

  • Multiple options for externalized configuration

 

Spring Boot Hello World


We will start with building our first Spring Boot application in this lesson. We will use Maven to manage dependencies.

The following steps are involved in starting up with a Spring Boot application:

  1. Configure spring-boot-starter-parent in your pom.xml file.

  2. Configure the pom.xml file with the required starter projects.

  3. Configure spring-boot-maven-plugin to be able to run the application.

  4. Create your first Spring Boot launch class.

Let's start with step 1, configuring the starter projects.

Configure spring-boot-starter-parent

Let's start with a simple pom.xml file with spring-boot-starter-parent:

    <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>
    <groupId>com.mastering.spring</groupId> 
    <artifactId>springboot-example</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 
    <name>First Spring Boot Example</name> 
    <packaging>war</packaging>
    <parent> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-parent</artifactId>  
      <version>2.0.0.M1</version>
    </parent>
    <properties> 
      <java.version>1.8</java.version> 
    </properties>

   <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
   </repositories>

   <pluginRepositories>
    <pluginRepository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
        <snapshots>
          <enabled>false</enabled>
        </snapshots>
     </pluginRepository>
    </pluginRepositories>

</project>

The first question is this: why do we need spring-boot-starter-parent?

A spring-boot-starter-parent dependency contains the default versions of Java to use, the default versions of dependencies that Spring Boot uses, and the default configuration of the Maven plugins.

Note

The spring-boot-starter-parent dependency is the parent POM providing dependency and plugin management for Spring Boot-based applications.

Let's look at some of the code inside spring-boot-starter-parent to get a deeper understanding about spring-boot-starter-parent.

spring-boot-starter-parent

The spring-boot-starter-parent dependency inherits from spring-boot-dependencies, which is defined at the top of the POM. The following code snippet shows an extract from spring-boot-starter-parent:

    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.0.0.M1</version>
      <relativePath>../../spring-boot-dependencies</relativePath>
   </parent>

The spring-boot-dependencies provides default dependency management for all the dependencies that Spring Boot uses. The following code shows the different versions of various dependencies that are configured in spring-boot-dependencies:

<activemq.version>5.13.4</activemq.version>
<aspectj.version>1.8.9</aspectj.version>
<ehcache.version>2.10.2.2.21</ehcache.version>
<elasticsearch.version>2.3.4</elasticsearch.version>
<gson.version>2.7</gson.version>
<h2.version>1.4.192</h2.version>
<hazelcast.version>3.6.4</hazelcast.version>
<hibernate.version>5.0.9.Final</hibernate.version>
<hibernate-validator.version>5.2.4.Final</hibernate
  validator.version>
<hsqldb.version>2.3.3</hsqldb.version>
<htmlunit.version>2.21</htmlunit.version>
<jackson.version>2.8.1</jackson.version>
<jersey.version>2.23.1</jersey.version>
<jetty.version>9.3.11.v20160721</jetty.version>
<junit.version>4.12</junit.version>
<mockito.version>1.10.19</mockito.version>
<selenium.version>2.53.1</selenium.version>
<servlet-api.version>3.1.0</servlet-api.version>
<spring.version>4.3.2.RELEASE</spring.version>
<spring-amqp.version>1.6.1.RELEASE</spring-amqp.version>
<spring-batch.version>3.0.7.RELEASE</spring-batch.version>
<spring-data-releasetrain.version>Hopper-SR2</spring-
  data-releasetrain.version>
<spring-hateoas.version>0.20.0.RELEASE</spring-hateoas.version>
<spring-restdocs.version>1.1.1.RELEASE</spring-restdocs.version>
<spring-security.version>4.1.1.RELEASE</spring-security.version>
<spring-session.version>1.2.1.RELEASE</spring-session.version>
<spring-ws.version>2.3.0.RELEASE</spring-ws.version>
<thymeleaf.version>2.1.5.RELEASE</thymeleaf.version>
<tomcat.version>8.5.4</tomcat.version>
<xml-apis.version>1.4.01</xml-apis.version>

If we want to override a specific version of a dependency, we can do that by providing a property with the right name in the pom.xml file of our application. The following code snippet shows an example of configuring our application to use version 1.10.20 of Mockito:

    <properties>
     <mockito.version>1.10.20</mockito.version>
    </properties>

The following are some of the other things defined in spring-boot-starter-parent:

  • The default Java version<java.version>1.8</java.version>

  • The default configuration for Maven plugins:

    • maven-failsafe-plugin

    • maven-surefire-plugin

    • git-commit-id-plugin

Compatibility between different versions of frameworks is one of the major problems faced by developers. How do I find the latest Spring Session version that is compatible with a specific version of Spring? The usual answer would be to read the documentation. However, if we use Spring Boot, this is made simple by spring-boot-starter-parent. If we want to upgrade to a newer Spring version, all that we need to do is to find the spring-boot-starter-parentdependency for that Spring version. Once we upgrade our application to use that specific version of spring-boot-starter-parent, we would have all the other dependencies upgraded to the versions compatible with the new Spring version. One less problem for developers to handle. Always make me happy.

Configure pom.xml with the Required Starter Projects

Whenever we want to build an application in Spring Boot, we would need to start looking for starter projects. Let's focus on understanding what a starter project is.

Understanding Starter Projects

Starters are simplified dependency descriptors customized for different purposes. For example, spring-boot-starter-web is the starter for building web application, including RESTful, using Spring MVC. It uses Tomcat as the default embedded container. If I want to develop a web application using Spring MVC, all we would need to do is include spring-boot-starter-web in our dependencies, and we get the following automatically pre-configured:

  • Spring MVC

  • Compatible versions of jackson-databind (for binding) and hibernate-validator (for form validation)

  • spring-boot-starter-tomcat (starter project for Tomcat)

The following code snippet shows some of the dependencies configured in spring-boot-starter-web:

    <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-validator</artifactId>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
       </dependency>
    </dependencies>

As we can see in the preceding snippet, when we usespring-boot-starter-web, we get a lot of frameworks auto-configured.

For the web application we would like to build, we would also want to do some good unit testing and deploy it on Tomcat. The following snippet shows the different starter dependencies that we would need. We would need to add this to our pom.xml file:

    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-tomcat</artifactId>
       <scope>provided</scope>
     </dependency>
    </dependencies>

We add three starter projects:

  • We've already discussed spring-boot-starter-web. It provides us with the frameworks needed to build a web application with Spring MVC.

  • The spring-boot-starter-test dependency provides the following test frameworks needed for unit testing:

    • JUnit: Basic unit test framework

    • Mockito: For mocking

    • Hamcrest, AssertJ: For readable asserts

    • Spring Test: A unit testing framework for spring-context based applications

  • The spring-boot-starter-tomcat dependency is the default for running web applications. We include it for clarity. The spring-boot-starter-tomcat is the starter for using Tomcat as the embedded servlet container.

We now have our pom.xml file configured with the starter parent and the required starter projects. Let's add spring-boot-maven-plugin now, which would enable us to run Spring Boot applications.

Configuring spring-boot-maven-plugin

When we build applications using Spring Boot, there are a couple of situations that are possible:

  • We would want to run the applications in place without building a JAR or a WAR

  • We would want to build a JAR and a WAR for later deployment

The spring-boot-maven-plugin dependency provides capabilities for both of the preceding situations. The following snippet shows how we can configure spring-boot-maven-plugin in an application:

    <build>
     <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
     </plugins>
    </build>

The spring-boot-maven-plugin dependency provides several goals for a Spring Boot application. The most popular goal is run (this can be executed as mvn spring-boot:run on the command prompt from the root folder of the project).

Creating Your First Spring Boot Launch Class

The following class explains how to create a simple Spring Boot launch class. It uses the static run method from the SpringApplication class, as shown in the following code snippet:

    package com.mastering.spring.springboot;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot
    autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationContext;
    @SpringBootApplication public class Application {
       public static void main(String[] args)
        { 
         ApplicationContext ctx = SpringApplication.run
         (Application.class,args);
        }
     }

The preceding code is a simple Java main method executing the static run method on the SpringApplication class.

The SpringApplication Class

The SpringApplication class can be used to Bootstrap and launch a Spring application from a Java main method.

The following are the steps that are typically performed when a Spring Boot application is bootstrapped:

  1. Create an instance of Spring's ApplicationContext.

  2. Enable the functionality to accept command-line arguments and expose them as Spring properties.

  3. Load all the Spring beans as per the configuration.

The @SpringBootApplication Annotation

The @SpringBootApplication annotation is a shortcut for three annotations:

  • @Configuration: Indicates that this a Spring application context configuration file.

  • @EnableAutoConfiguration: Enables auto-configuration, an important feature of Spring Boot. We will discuss auto-configuration later in a separate section.

  • @ComponentScan: Enables scanning for Spring beans in the package of this class and all its sub packages.

Running Our Hello World Application

We can run the Hello World application in multiple ways. Let's start running it with the simplest option--running as a Java application. In your IDE, right-click on the application class and run it as Java Application. The following screenshot shows some of the log from running our Hello World application:

The following are the key things to note:

  • Tomcat server is launched on port 8080--Tomcat started on port(s): 8080 (http).

  • DispatcherServlet is configured. This means that Spring MVC Framework is ready to accept requests--Mapping servlet: 'dispatcherServlet' to [/].

  • Four filters--characterEncodingFilter, hiddenHttpMethodFilter, httpPutFormContentFilter and requestContextFilter--are enabled by default

  • The default error page is configured--Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)

  • WebJars are autoconfigured. WebJars enable dependency management for static dependencies such as Bootstrap and query--Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]

The following screenshot shows the application layout as of now. We have just two files, pom.xml and Application.java:

With a simple pom.xml file and one Java class, we were able to get to launch the Spring MVC application, with all the preceding functionality described. The most important thing about Spring Boot is to understand what happens in the background. Understanding the preceding start up log is the first. Let's look at the Maven dependencies to get a deeper picture.

The following screenshot shows some of the dependencies that are configured with the basic configuration in the pom.xml file that we created:

Spring Boot does a lot of magic. Once you have the application configured and running, I recommend that you play around with it to gain a deeper understanding that will be useful when you are debugging problems.

As Spiderman says, with great power, comes great responsibility. This is absolutely true in the case of Spring Boot. In the time to come, the best developers with Spring Boot would be the ones who understand what happens in the background--dependencies and auto-configuration.

Auto-configuration

To enable us to understand auto-configuration further, let's expand our application class to include a few more lines of code:

    ApplicationContext ctx = SpringApplication.run(Application.class,
     args);
    String[] beanNames = ctx.getBeanDefinitionNames();
    Arrays.sort(beanNames);

   for (String beanName : beanNames) {
     System.out.println(beanName);
    }

We get all the beans that are defined in the Spring application context and print their names. When Application.java is run as a Java program, it prints the list of beans, as shown in the following output:

application
basicErrorController
beanNameHandlerMapping
beanNameViewResolver
characterEncodingFilter
conventionErrorViewResolver
defaultServletHandlerMapping
defaultViewResolver
dispatcherServlet
dispatcherServletRegistration
duplicateServerPropertiesDetector
embeddedServletContainerCustomizerBeanPostProcessor
error
errorAttributes
errorPageCustomizer
errorPageRegistrarBeanPostProcessor
faviconHandlerMapping
faviconRequestHandler
handlerExceptionResolver
hiddenHttpMethodFilter
httpPutFormContentFilter
httpRequestHandlerAdapter
jacksonObjectMapper
jacksonObjectMapperBuilder
jsonComponentModule
localeCharsetMappingsCustomizer
mappingJackson2HttpMessageConverter
mbeanExporter
mbeanServer
messageConverters
multipartConfigElement
multipartResolver
mvcContentNegotiationManager
mvcConversionService
mvcPathMatcher
mvcResourceUrlProvider
mvcUriComponentsContributor
mvcUrlPathHelper
mvcValidator
mvcViewResolver
objectNamingStrategy
autoconfigure.AutoConfigurationPackages
autoconfigure.PropertyPlaceholderAutoConfiguration
autoconfigure.condition.BeanTypeRegistry
autoconfigure.context.ConfigurationPropertiesAutoConfiguration
autoconfigure.info.ProjectInfoAutoConfiguration
autoconfigure.internalCachingMetadataReaderFactory
autoconfigure.jackson.JacksonAutoConfiguration
autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration
autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration
autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration
autoconfigure.jmx.JmxAutoConfiguration
autoconfigure.web.DispatcherServletAutoConfiguration
autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration
autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration
autoconfigure.web.EmbeddedServletContainerAutoConfiguration
autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat
autoconfigure.web.ErrorMvcAutoConfiguration
autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration
autoconfigure.web.HttpEncodingAutoConfiguration
autoconfigure.web.HttpMessageConvertersAutoConfiguration
autoconfigure.web.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration
autoconfigure.web.JacksonHttpMessageConvertersConfiguration
autoconfigure.web.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration
autoconfigure.web.MultipartAutoConfiguration
autoconfigure.web.ServerPropertiesAutoConfiguration
autoconfigure.web.WebClientAutoConfiguration
autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfiguration
autoconfigure.web.WebMvcAutoConfiguration
autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration
autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration
autoconfigure.websocket.WebSocketAutoConfiguration
autoconfigure.websocket.WebSocketAutoConfiguration$TomcatWebSocketConfiguration
context.properties.ConfigurationPropertiesBindingPostProcessor
context.properties.ConfigurationPropertiesBindingPostProcessor.store
annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
annotation.ConfigurationClassPostProcessor.importAwareProcessor
annotation.internalAutowiredAnnotationProcessor
annotation.internalCommonAnnotationProcessor
annotation.internalConfigurationAnnotationProcessor
annotation.internalRequiredAnnotationProcessor
event.internalEventListenerFactory
event.internalEventListenerProcessor
preserveErrorControllerTargetClassPostProcessor
propertySourcesPlaceholderConfigurer
requestContextFilter
requestMappingHandlerAdapter
requestMappingHandlerMapping
resourceHandlerMapping
restTemplateBuilder
serverProperties
simpleControllerHandlerAdapter
spring.http.encoding-autoconfigure.web.HttpEncodingProperties
spring.http.multipart-autoconfigure.web.MultipartProperties
spring.info-autoconfigure.info.ProjectInfoProperties
spring.jackson-autoconfigure.jackson.JacksonProperties
spring.mvc-autoconfigure.web.WebMvcProperties
spring.resources-autoconfigure.web.ResourceProperties
standardJacksonObjectMapperBuilderCustomizer
stringHttpMessageConverter
tomcatEmbeddedServletContainerFactory
viewControllerHandlerMapping
viewResolver
websocketContainerCustomizer

Important things to think about are as follows:

  • Where are these beans defined?

  • How are these beans created?

That's the magic of Spring auto-configuration.

Whenever we add a new dependency to a Spring Boot project, Spring Boot auto-configuration automatically tries to configure the beans based on the dependency.

For example, when we add a dependency in spring-boot-starter-web, the following beans are auto-configured:

  • basicErrorController, handlerExceptionResolver: It is the basic exception handling. It shows a default error page when an exception occurs.

  • beanNameHandlerMapping: It is used to resolve paths to a handler (controller).

  • characterEncodingFilter: It provides default character encoding UTF-8.

  • dispatcherServlet: It is the front controller in Spring MVC applications.

  • jacksonObjectMapper: It translates objects to JSON and JSON to objects in REST services.

  • messageConverters: It is the default message converters to convert from objects into XML or JSON and vice versa.

  • multipartResolver: It provides support to upload files in web applications.

  • mvcValidator: It supports validation of HTTP requests.

  • viewResolver: It resolves a logical view name to a physical view.

  • propertySourcesPlaceholderConfigurer: It supports the externalization of application configuration.

  • requestContextFilter: It defaults the filter for requests.

  • restTemplateBuilder: It is used to make calls to REST services.

  • tomcatEmbeddedServletContainerFactory: Tomcat is the default embedded servlet container for Spring Boot-based web applications.

In the next section, let's look at some of the starter projects and the auto-configuration they provide.

Starter Projects

The following table shows some of the important starter projects provided by Spring Boot:

Starter

Description

spring-boot-starter-webservices

This is a starter project to develop XMLbased web services.

spring-boot-starter-web

This is a starter project to build Spring MVC-based web applications or RESTful applications. It uses Tomcat as the default embedded servlet container.

spring-boot-starter-activemq

This supports message-based communication using JMS on ActiveMQ.

spring-boot-starterintegration

This supports the Spring Integration Framework that provides implementations for Enterprise Integration Patterns.

spring-boot-starter-test

This provides support for various unit testing frameworks, such as JUnit, Mockito, and Hamcrest matchers.

spring-boot-starter-jdbc

This provides support for using Spring JDBC. It configures a Tomcat JDBC connection pool by default.

spring-boot-startervalidation

This provides support for the Java Bean Validation API. Its default implementation is hibernate-validator.

spring-boot-starter-hateoas

HATEOAS stands for Hypermedia as the Engine of Application State. RESTful services that use HATEOAS return links to additional resources that are related to the current context in addition to data.

spring-boot-starter-jersey

JAX-RS is the Java EE standard to develop REST APIs. Jersey is the default implementation. This starter project provides support to build JAX-RS-based REST APIs.

spring-boot-starterwebsocket

HTTP is stateless. WebSockets allow you to maintain a connection between the server and the browser. This starter project provides support for Spring WebSockets.

spring-boot-starter-aop

This provides support for Aspect Oriented Programming. It also provides support for AspectJ for advanced aspect-oriented programming.

spring-boot-starter-amqp

With RabbitMQ as the default, this starter project provides message passing with AMQP.

spring-boot-starter-security

This starter project enables auto-configuration for Spring Security.

spring-boot-starter-data-jpa

This provides support for Spring Data JPA. Its default implementation is Hibernate.

spring-boot-starter

This is a base starter for Spring Boot applications. It provides support for auto-configuration and logging.

spring-boot-starter-batch

This provides support to develop batch applications using Spring Batch.

spring-boot-starter-cache

This is the basic support for caching using Spring Framework.

spring-boot-starter-datarest

This is the support to expose REST services using Spring Data REST.

Until now, we have set up a basic web application and understood some of the important concepts related to Spring Boot:

  • Auto-configuration

  • Starter projects

  • spring-boot-maven-plugin

  • spring-boot-starter-parent

  • Annotation @SpringBootApplication

Now let's shift our focus to understanding what REST is and building a REST Service.

 

What is REST?


Representational State Transfer (REST) is basically an architectural style for the web. REST specifies a set of constraints. These constraints ensure that clients (service consumers and browsers) can interact with servers in flexible ways.

Let's first understand some common terminologies:

  • Server: Service provider. Exposes services which can be consumed by clients.

  • Client: Service consumer. Could be a browser or another system.

  • Resource: Any information can be a resource: a person, an image, a video, or a product you want to sell.

  • Representation: A specific way a resource can be represented. For example, the product resource can be represented using JSON, XML, or HTML. Different clients might request different representations of the resource.

Some of the important REST constraints are listed as follows:

  • Client-Server: There should be a server (service provider) and a client (service consumer). This enables loose coupling and independent evolution of the server and client as new technologies emerge.

  • Stateless: Each service should be stateless. Subsequent requests should not depend on some data from a previous request being temporarily stored. Messages should be self-descriptive.

  • Uniform interface: Each resource has a resource identifier. In the case of web services, we use this URI example: /users/Jack/todos/1. In this, URI Jack is the name of the user. 1 is the ID of the todo we would want to retrieve.

  • Cacheable: The service response should be cacheable. Each response should indicate whether it is cacheable.

  • Layered system: The consumer of the service should not assume a direct connection to the service provider. Since requests can be cached, the client might be getting the cached response from a middle layer.

  • Manipulation of resources through representations: A resource can have multiple representations. It should be possible to modify the resource through a message with any of these representations.

  • Hypermedia as the engine of application state (HATEOAS): The consumer of a RESTful application should know about only one fixed service URL. All subsequent resources should be discoverable from the links included in the resource representations.

An example response with the HATEOAS link is shown here. This is the response to a request to retrieve all todos:

    {  
"_embedded":{ 
"todos":[ 
{ 
"user":"Jill",
"desc":"Learn Hibernate",
"done":false,
"_links":{ 
"self":{ 
"href":"http://localhost:8080/todos/1"
                  },
"todo":{ 
"href":"http://localhost:8080/todos/1"
}
}
}
]
},
"_links":{ 
"self":{ 
"href":"http://localhost:8080/todos"
},
"profile":{ 
"href":"http://localhost:8080/profile/todos"
},
"search":{ 
"href":"http://localhost:8080/todos/search"
}
}
    }

The preceding response includes links to the following:

  • Specific todos (http://localhost:8080/todos/1)

  • Search resource (http://localhost:8080/todos/search)

If the service consumer wants to do a search, it has the option of taking the search URL from the response and sending the search request to it. This would reduce coupling between the service provider and the service consumer.

The initial services we develop will not be adhering to all these constraints. As we move on to the next lessons, we will introduce you to the details of these constraints and add them to the services to make them more RESTful.

 

First REST Service


Let's start with creating a simple REST service returning a welcome message. We will create a simple POJO WelcomeBean class with a member field called message and one argument constructor, as shown in the following code snippet:

    package com.mastering.spring.springboot.bean;

    public class WelcomeBean {
      private String message;

       public WelcomeBean(String message) {
         super();
         this.message = message;
       }

      public String getMessage() {
        return message;
      }
    }

Simple Method Returning String

Let's start with creating a simple REST Controller method returning a string:

    @RestController
    public class BasicController {
      @GetMapping("/welcome")
      public String welcome() {
        return "Hello World";
      }
    }

A few important things to note are as follows:

  • @RestController: The @RestController annotation provides a combination of @ResponseBody and @Controller annotations. This is typically used to create REST Controllers.

  • @GetMapping("welcome"): @GetMapping is a shortcut for @RequestMapping(method = RequestMethod.GET). This annotation is a readable alternative. The method with this annotation would handle a Get request to the welcome URI.

If we run Application.java as a Java application, it would start up the embedded Tomcat container. We can launch up the URL in the browser, as shown in the following screenshot:

Unit Testing

Let's quickly write a unit test to test the preceding controller method:

    @RunWith(SpringRunner.class)
    @WebMvcTest(BasicController.class)
    public class BasicControllerTest {

      @Autowired
      private MockMvc mvc;

      @Test
      public void welcome() throws Exception {
        mvc.perform(
        MockMvcRequestBuilders.get("/welcome")
       .accept(MediaType.APPLICATION_JSON))
       .andExpect(status().isOk())
       .andExpect(content().string(
       equalTo("Hello World")));
      }
    }

In the preceding unit test, we will launch up a Mock MVC instance with BasicController. A few quick things to note are as follows:

  • @RunWith(SpringRunner.class): SpringRunner is a shortcut to the SpringJUnit4ClassRunner annotation. This launches up a simple Spring context for unit testing.

  • @WebMvcTest(BasicController.class): This annotation can be used along with SpringRunner to write simple tests for Spring MVC controllers. This will load only the beans annotated with Spring-MVC-related annotations. In this example, we are launching a Web MVC Test context with the class under test being BasicController.

  • @Autowired private MockMvc mvc: Autowires the MockMvc bean that can be used to make requests.

  • mvc.perform(MockMvcRequestBuilders.get("/welcome").accept(MediaType.APPLICATION_JSON)): Performs a request to /welcome with the Accept header value application/json.

  • andExpect(status().isOk()): Expects that the status of the response is 200 (success).

  • andExpect(content().string(equalTo("Hello World"))): Expects that the content of the response is equal to "Hello World".

Integration Testing

When we do integration testing, we would want to launch the embedded server with all the controllers and beans that are configured. This code snippet shows how we can create a simple integration test:

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = Application.class, 
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class BasicControllerIT {

      private static final String LOCAL_HOST = 
      "http://localhost:";

      @LocalServerPort
      private int port;

      private TestRestTemplate template = new TestRestTemplate();

      @Test
      public void welcome() throws Exception {
        ResponseEntity<String> response = template
       .getForEntity(createURL("/welcome"), String.class);
        assertThat(response.getBody(), equalTo("Hello World"));
       }

      private String createURL(String uri) {
        return LOCAL_HOST + port + uri;
      }
    }

A few important things to note are as follows:

  • @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT): It provides additional functionality on top of the Spring TestContext. Provides support to configure the port for fully running the container and TestRestTemplate (to execute requests).

  • @LocalServerPort private int port: The SpringBootTest would ensure that the port on which the container is running is autowired into the port variable.

  • private String createURL(String uri): The method to append the local host URL and port to the URI to create a full URL.

  • private TestRestTemplate template = new TestRestTemplate(): The TestRestTemplate is typically used in integration tests. It provides additional functionality on top of RestTemplate, which is especially useful in the integration test context. It does not follow redirects so that we can assert response location.

  • template.getForEntity(createURL("/welcome"), String.class): It executes a get request for the given URI.

  • assertThat(response.getBody(), equalTo("Hello World")): It asserts that the response body content is "Hello World".

Simple REST Method Returning an Object

In the previous method, we returned a string. Let's create a method that returns a proper JSON response. Take a look at the following method:

    @GetMapping("/welcome-with-object")
    public WelcomeBean welcomeWithObject() {
      return new WelcomeBean("Hello World");
    }

This preceding method returns a simple WelcomeBean initialized with a message: "Hello World".

Executing a Request

Let's send a test request and see what response we get. The following screenshot shows the output:

The response for the http://localhost:8080/welcome-with-object URL is shown as follows:

    {"message":"Hello World"}

The question that needs to be answered is this: how does the WelcomeBean object that we returned get converted into JSON?

Again, it's the magic of Spring Boot auto-configuration. If Jackson is on the classpath of an application, instances of the default object to JSON (and vice versa) converters are auto-configured by Spring Boot.

Unit Testing

Let's quickly write a unit test checking for the JSON response. Let's add the test to BasicControllerTest:

    @Test
    public void welcomeWithObject() throws Exception {
      mvc.perform(
       MockMvcRequestBuilders.get("/welcome-with-object")
      .accept(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(content().string(containsString("Hello World")));
    }

This test is very similar to the earlier unit test except that we are using containsString to check whether the content contains a substring "Hello World". We will learn how to write proper JSON tests a little later.

Integration Testing

Let's shift our focus to writing an integration test. Let's add a method to BasicControllerIT, as shown in the following code snippet:

    @Test
    public void welcomeWithObject() throws Exception {
      ResponseEntity<String> response = 
      template.getForEntity(createURL("/welcome-with-object"), 
      String.class);
      assertThat(response.getBody(), 
      containsString("Hello World"));
    }

This method is similar to the earlier integration test except that we are asserting for a sub-string using the Stringmethod.

Get Method with Path Variables

Let's shift our attention to path variables. Path variables are used to bind values from the URI to a variable on the controller method. In the following example, we want to parameterize the name so that we can customize the welcome message with a name:

    private static final String helloWorldTemplate = "Hello World, 
    %s!";

   @GetMapping("/welcome-with-parameter/name/{name}")
   public WelcomeBean welcomeWithParameter(@PathVariable String name) 
    {
       return new WelcomeBean(String.format(helloWorldTemplate, name));
    }

A few important things to note are as follows:

  • @GetMapping("/welcome-with-parameter/name/{name}"): {name} indicates that this value will be the variable. We can have multiple variable templates in a URI.

  • welcomeWithParameter(@PathVariable String name): @PathVariable ensures that the variable value from the URI is bound to the variable name.

  • String.format(helloWorldTemplate, name): A simple string format to replace %s in the template with the name.

Executing a Request

Let's send a test request and see what response we get. The following screenshot shows the response:

The response for the http://localhost:8080/welcome-with-parameter/name/Buddy URL is as follows:

    {"message":"Hello World, Buddy!"}

As expected, the name in the URI is used to form the message in the response.

Unit Testing

Let's quickly write a unit test for the preceding method. We would want to pass a name as part of the URI and check whether the response contains the name. The following code shows how we can do that:

    @Test
    public void welcomeWithParameter() throws Exception {
      mvc.perform(
      MockMvcRequestBuilders.get("/welcome-with-parameter/name/Buddy")
     .accept(MediaType.APPLICATION_JSON))
     .andExpect(status().isOk())
     .andExpect(
     content().string(containsString("Hello World, Buddy")));
    }

A few important things to note are as follows:

  • MockMvcRequestBuilders.get("/welcome-with-parameter/name/Buddy"): This matches against the variable template in the URI. We pass in the name Buddy.

  • .andExpect(content().string(containsString("Hello World, Buddy"))): We expect the response to contain the message with the name.

Integration Testing

The integration test for the preceding method is very simple. Take a look at the following test method:

    @Test
    public void welcomeWithParameter() throws Exception {
      ResponseEntity<String> response = 
      template.getForEntity(
      createURL("/welcome-with-parameter/name/Buddy"), String.class);
      assertThat(response.getBody(), 
      containsString("Hello World, Buddy"));
    }

A few important things to note are as follows:

  • createURL("/welcome-with-parameter/name/Buddy"): This matches against the variable template in the URI. We are passing in the name, Buddy.

  • assertThat(response.getBody(), containsString("Hello World, Buddy")): We expect the response to contain the message with the name.

In this section, we looked at the basics of creating a simple REST service with Spring Boot. We also ensured that we have good unit tests and integration tests. While these are really basic, they lay the foundation for more complex REST services we will build in the next section.

The unit tests and integration tests we implemented can have better asserts using a JSON comparison instead of a simple substring comparison. We will focus on it in the tests we write for the REST services we will create in the next sections.

 

Creating a Todo Resource


We will focus on creating REST services for a basic todo management system. We will create services for the following:

  • Retrieving a list of todos for a given user

  • Retrieving details for a specific todo

  • Creating a todo for a user

Request Methods, Operations, and Uris

One of the best practices of REST services is to use the appropriate HTTP request method based on the action we perform. In the services we exposed until now, we used the GET method, as we focused on services that read data.

The following table shows the appropriate HTTP Request method based on the operation that we perform:

HTTP Request Method

Operation

GET

Read--Retrieve details for a resource

POST

Create--Create a new item or resource

PUT

Update/replace

PATCH

Update/modify a part of the resource

DELETE

Delete

Let's quickly map the services that we want to create to the appropriate request methods:

  • Retrieving a list of todos for a given user: This is READ. We will use GET. We will use a URI: /users/{name}/todos. One more good practice is to use plurals for static things in the URI: users, todo, and so on. This results in more readable URIs.

  • Retrieving details for a specific todo: Again, we will use GET. We will use a URI /users/{name}/todos/{id}. You can see that this is consistent with the earlier URI that we decided for the list of todos.

  • Creating a todo for a user: For the create operation, the suggested HTTP Request method is POST. To create a new todo, we will post to URI /users/{name}/todos.

Beans and Services

To be able to retrieve and store details of a todo, we need a Todo bean and a service to retrieve and store the details.

Let's create a Todo Bean:

    public class Todo {
      private int id;
      private String user;

      private String desc;

      private Date targetDate;
      private boolean isDone;

      public Todo() {}

      public Todo(int id, String user, String desc, 
      Date targetDate, boolean isDone) { 
        super();
        this.id = id;
        this.user = user;
        this.desc = desc;
        this.targetDate = targetDate;
        this.isDone = isDone;
      }

       //ALL Getters
    }

We have a created a simple Todo bean with the ID, the name of user, the description of the todo, the todo target date, and an indicator for the completion status. We added a constructor and getters for all fields.

Let's add TodoService now:

   @Service
   public class TodoService {
     private static List<Todo> todos = new ArrayList<Todo>();
     private static int todoCount = 3;

     static {
       todos.add(new Todo(1, "Jack", "Learn Spring MVC", 
       new Date(), false));
       todos.add(new Todo(2, "Jack", "Learn Struts", new Date(), 
       false));
       todos.add(new Todo(3, "Jill", "Learn Hibernate", new Date(), 
       false));
      }

     public List<Todo> retrieveTodos(String user) {
       List<Todo> filteredTodos = new ArrayList<Todo>();
       for (Todo todo : todos) {
         if (todo.getUser().equals(user))
         filteredTodos.add(todo);
        }
      return filteredTodos;
     }

    public Todo addTodo(String name, String desc, 
    Date targetDate, boolean isDone) {
      Todo todo = new Todo(++todoCount, name, desc, targetDate, 
      isDone);
      todos.add(todo);
      return todo;
    }

    public Todo retrieveTodo(int id) {
      for (Todo todo : todos) {
      if (todo.getId() == id)
        return todo;
      }
      return null;
     }
   }

Quick things to note are as follows:

  • To keep things simple, this service does not talk to the database. It maintains an in-memory array list of todos. This list is initialized using a static initializer.

  • We are exposing a couple of simple retrieve methods and a method to add a to-do.

Now that we have the service and bean ready, we can create our first service to retrieve a list of to-do's for a user.

Retrieving a Todo List

We will create a new RestController annotation called TodoController. The code for the retrieve todos method is shown as follows:

    @RestController
    public class TodoController {
     @Autowired
     private TodoService todoService;

     @GetMapping("/users/{name}/todos")
     public List<Todo> retrieveTodos(@PathVariable String name) {
       return todoService.retrieveTodos(name);
     }
    }

A couple of things to note are as follows:

  • We are autowiring the todo service using the @Autowired annotation

  • We use the @GetMapping annotation to map the Get request for the "/users/{name}/todos" URI to the retrieveTodos method

Executing the Service

Let's send a test request and see what response we get. The following screenshot shows the output:

The response for the http://localhost:8080/users/Jack/todos URL is as follows:

   [
    {"id":1,"user":"Jack","desc":"Learn Spring    
     MVC","targetDate":1481607268779,"done":false},  
    {"id":2,"user":"Jack","desc":"Learn 
    Struts","targetDate":1481607268779, "done":false}
   ]

Unit Testing

The code to unit test the TodoController class is shown in the following screenshot:

   @RunWith(SpringRunner.class)
   @WebMvcTest(TodoController.class)
   public class TodoControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private TodoService service;

    @Test
    public void retrieveTodos() throws Exception {
     List<Todo> mockList = Arrays.asList(new Todo(1, "Jack",
     "Learn Spring MVC", new Date(), false), new Todo(2, "Jack",
     "Learn Struts", new Date(), false));

     when(service.retrieveTodos(anyString())).thenReturn(mockList);

     MvcResult result = mvc
    .perform(MockMvcRequestBuilders.get("/users
    /Jack/todos").accept(MediaType.APPLICATION_JSON))
    .andExpect(status().isOk()).andReturn();

    String expected = "["
     + "{id:1,user:Jack,desc:\"Learn Spring MVC\",done:false}" +","
     + "{id:2,user:Jack,desc:\"Learn Struts\",done:false}" + "]";

     JSONAssert.assertEquals(expected, result.getResponse()
      .getContentAsString(), false);
     }
    }

A few important things to note are as follows:

  • We are writing a unit test. So, we want to test only the logic present in the TodoController class. So, we initialize a Mock MVC framework with only the TodoController class using @WebMvcTest(TodoController.class).

  • @MockBean private TodoService service: We are mocking out the TodoService using the @MockBeanannotation. In test classes that are run with SpringRunner, the beans defined with @MockBean will be replaced by a mock, created using the Mockito framework.

  • when(service.retrieveTodos(anyString())).thenReturn(mockList): We are mocking the retrieveTodos service method to return the mock list.

  • MvcResult result = ..: We are accepting the result of the request into an MvcResult variable to enable us to perform assertions on the response.

  • JSONAssert.assertEquals(expected, result.getResponse().getContentAsString(), false): JSONAssert is a very useful framework to perform asserts on JSON. It compares the response text with the expected value. JSONAssert is intelligent enough to ignore values that are not specified. Another advantage is a clear failure message in case of assertion failures. The last parameter, false, indicates using non-strict mode. If it is changed to true, then the expected should exactly match the result.

Integration Testing

The code to perform integration testing on the TodoController class is shown in the following code snippet. It launches up the entire Spring context with all the controllers and beans defined:

   @RunWith(SpringJUnit4ClassRunner.class)
   @SpringBootTest(classes = Application.class, webEnvironment =     
   SpringBootTest.WebEnvironment.RANDOM_PORT)
   public class TodoControllerIT {

    @LocalServerPort
    private int port;

    private TestRestTemplate template = new TestRestTemplate();

    @Test
    public void retrieveTodos() throws Exception {
     String expected = "["
     + "{id:1,user:Jack,desc:\"Learn Spring MVC\",done:false}" + ","
     + "{id:2,user:Jack,desc:\"Learn Struts\",done:false}" + "]";

     String uri = "/users/Jack/todos";

     ResponseEntity<String> response =
     template.getForEntity(createUrl(uri), String.class);

     JSONAssert.assertEquals(expected, response.getBody(), false);
    }

     private String createUrl(String uri) {
     return "http://localhost:" + port + uri;
    }
  }

This test is very similar to the integration test for BasicController, except that we are using JSONAssert to assert the response.

Retrieving Details for a Specific Todo

We will now add the method to retrieve details for a specific Todo:

    @GetMapping(path = "/users/{name}/todos/{id}")
    public Todo retrieveTodo(@PathVariable String name, @PathVariable 
    int id) {
      return todoService.retrieveTodo(id);
    }

A couple of things to note are as follows:

  • The URI mapped is /users/{name}/todos/{id}

  • We have two path variables defined for name and id

Executing the Service

Let's send a test request and see what response we will get, as shown in the following screenshot:

The response for the http://localhost:8080/users/Jack/todos/1 URL is shown as follows:

    {"id":1,"user":"Jack","desc":"Learn Spring MVC", 
    "targetDate":1481607268779,"done":false}

Unit Testing

The code to unit test retrieveTodo is as follows:

     @Test
     public void retrieveTodo() throws Exception {
       Todo mockTodo = new Todo(1, "Jack", "Learn Spring MVC", 
       new Date(), false);

       when(service.retrieveTodo(anyInt())).thenReturn(mockTodo);

       MvcResult result = mvc.perform(
       MockMvcRequestBuilders.get("/users/Jack/todos/1")
       .accept(MediaType.APPLICATION_JSON))
       .andExpect(status().isOk()).andReturn();

       String expected = "{id:1,user:Jack,desc:\"Learn Spring
       MVC\",done:false}";

      JSONAssert.assertEquals(expected, 
       result.getResponse().getContentAsString(), false);

     }

A few important things to note are as follows:

  • when(service.retrieveTodo(anyInt())).thenReturn(mockTodo): We are mocking the retrieveTodo service method to return the mock todo.

  • MvcResult result = ..: We are accepting the result of the request into an MvcResult variable to enable us to perform assertions on the response.

  • JSONAssert.assertEquals(expected, result.getResponse().getContentAsString(), false): Asserts whether the result is as expected.

Integration Testing

The code to perform integration testing on retrieveTodos in TodoController is shown in the following code snippet. This would be added to the TodoControllerIT class:

     @Test
     public void retrieveTodo() throws Exception {
       String expected = "{id:1,user:Jack,desc:\"Learn Spring   
       MVC\",done:false}";
       ResponseEntity<String> response = template.getForEntity(
       createUrl("/users/Jack/todos/1"), String.class);
       JSONAssert.assertEquals(expected, response.getBody(), false);
     }

Adding A Todo

We will now add the method to create a new Todo. The HTTP method to be used for creation is Post. We will post to a "/users/{name}/todos" URI:

    @PostMapping("/users/{name}/todos")
    ResponseEntity<?> add(@PathVariable String name,
    @RequestBody Todo todo) { 
      Todo createdTodo = todoService.addTodo(name, todo.getDesc(),
      todo.getTargetDate(), todo.isDone());
      if (createdTodo == null) {
         return ResponseEntity.noContent().build();
      }

     URI location = ServletUriComponentsBuilder.fromCurrentRequest()

    .path("/{id}").buildAndExpand(createdTodo.getId()).toUri();
    return ResponseEntity.created(location).build();
   }

A few things to note are as follows:

  • @PostMapping("/users/{name}/todos"): @PostMapping annotations map the add() method to the HTTP Request with a POST method.

  • ResponseEntity<?> add(@PathVariable String name, @RequestBody Todo todo): An HTTP post request should ideally return the URI to the created resources. We use ResourceEntity to do this. @RequestBody binds the body of the request directly to the bean.

  • ResponseEntity.noContent().build(): Used to return that the creation of the resource failed.

  • ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(createdTodo.getId()).toUri(): Forms the URI for the created resource that can be returned in the response.

  • ResponseEntity.created(location).build(): Returns a status of 201(CREATED) with a link to the resource created.

Postman

If you are on Mac, you might want to try the Paw application as well.

Let's send a test request and see what response we get. The following screenshot shows the response:

We will use Postman app to interact with the REST Services. You can install it from the website, https://www.getpostman.com/. It is available on Windows and Mac. A Google Chrome plugin is also available.

Executing the POST Service

To create a new Todo using POST, we would need to include the JSON for the Todo in the body of the request. The following screenshot shows how we can use the Postman app to create the request and the response after executing the request:

A few important things to note are as follows:

  • We are sending a POST request. So, we choose the POST from the top-left dropdown.

  • To send the Todo JSON as part of the body of the request, we select the raw option in the Body tab (highlighted with a blue dot). We choose the content type as JSON (application/json).

  • Once the request is successfully executed, you can see the status of the request in the bar in the middle of the screen: Status: 201 Created.

  • The location is http://localhost:8080/users/Jack/todos/5. This is the URI of the newly created todo that is received in the response.

Complete details of the request to http://localhost:8080/users/Jack/todos are shown in the block, as follows:

    Header
    Content-Type:application/json

   Body
    {
      "user": "Jack",
      "desc": "Learn Spring Boot",
       "done": false
     }

Unit Testing

The code to unit test the created Todo is shown as follows:

    @Test
    public void createTodo() throws Exception {
     Todo mockTodo = new Todo(CREATED_TODO_ID, "Jack", 
     "Learn Spring MVC", new Date(), false);
     String todo = "{"user":"Jack","desc":"Learn Spring MVC",     
     "done":false}";

    when(service.addTodo(anyString(), anyString(),   
    isNull(),anyBoolean()))
    .thenReturn(mockTodo);

   mvc
    .perform(MockMvcRequestBuilders.post("/users/Jack/todos")
    .content(todo)
    .contentType(MediaType.APPLICATION_JSON)
    )
    .andExpect(status().isCreated())
    .andExpect(
      header().string("location",containsString("/users/Jack/todos/"
     + CREATED_TODO_ID)));
   }

A few important things to note are as follows:

  • String todo = "{"user":"Jack","desc":"Learn Spring MVC","done":false}": The Todo content to post to the create todo service.

  • when(service.addTodo(anyString(), anyString(), isNull(), anyBoolean())).thenReturn(mockTodo): Mocks the service to return a dummy todo.

  • MockMvcRequestBuilders.post("/users/Jack/todos").content(todo).contentType(MediaType.APPLICATION_JSON)): Creates a POST to a given URI with the given content type.

  • andExpect(status().isCreated()): Expects that the status is created.

  • andExpect(header().string("location",containsString("/users/Jack/todos/" + CREATED_TODO_ID))): Expects that the header contains location with the URI of created resource.

Integration Testing

The code to perform integration testing on the created todo in TodoController is shown as follows. This would be added to the TodoControllerIT class, as follows:

    @Test
    public void addTodo() throws Exception {
      Todo todo = new Todo(-1, "Jill", "Learn Hibernate", new Date(),  
      false);
      URI location = template
     .postForLocation(createUrl("/users/Jill/todos"),todo);
      assertThat(location.getPath(), 
      containsString("/users/Jill/todos/4"));
    }

A few important things to note are as follows:

  • URI location = template.postForLocation(createUrl("/users/Jill/todos"), todo): postForLocation is a utility method especially useful in tests to create new resources. We are posting the todo to the given URI and getting the location from the header.

  • assertThat(location.getPath(), containsString("/users/Jill/todos/4")): Asserts that the location contains the path to the newly created resource.

 

Spring Initializr


Do you want to auto-generate Spring Boot projects? Do you want to quickly get started with developing your application? Spring Initializr is the answer.

Spring Initializr is hosted at http://start.spring.io. The following screenshot shows how the website looks:

Spring Initializr provides a lot of flexibility in creating projects. You have options to do the following:

  • Choose your build tool: Maven or Gradle.

  • Choose the Spring Boot version you want to use.

  • Configure a Group ID and Artifact ID for your component.

  • Choose the starters (dependencies) that you would want for your project. You can click on the link at the bottom of the screen, Switch to the full version, to see all the starter projects you can choose from.

  • Choose how to package your component: JAR or WAR.

  • Choose the Java version you want to use.

  • Choose the JVM language you want to use.

The following screenshot shows some of the options Spring Initializr provides when you expand (click on the link) to the full version:

Creating Your First Spring Initializr Project

We will use the full version and enter the values, as follows:

Things to note are as follows:

  • Build tool: Maven

  • Spring Boot version: Choose the latest available

  • Group: com.mastering.spring

  • Artifact: first-spring-initializr

  • Selected dependencies: Choose Web, JPA, Actuator and Dev Tools. Type in each one of these in the textbox and press Enter to choose them. We will learn more about Actuator and Dev Tools in the next section

  • Java version: 1.8

Go ahead and click on the Generate Project button. This will create a .zip file and you can download it to your computer.

The following screenshot shows the structure of the project created:

We will now import this project into your IDE. In Eclipse, you can perform the following steps:

  1. Launch Eclipse.

  2. Navigate to File | Import.

  3. Choose the existing Maven projects.

  4. Browse and select the folder that is the root of the Maven project (the one containing the pom.xml file).

  5. Proceed with the defaults and click on Finish .

This will import the project into Eclipse. The following screenshot shows the structure of the project in Eclipse:

Let's look at some of the important files from the generated project.

pom.xml

The following snippet shows the dependencies that are declared:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

A few other important observations are as follows:

  • The packaging for this component is .jar

  • org.springframework.boot:spring-boot-starter-parent is declared as the parent POM

  • <java.version>1.8</java.version>: The Java version is 1.8

  • Spring Boot Maven Plugin (org.springframework.boot:spring-boot-maven-plugin) is configured as a plugin

FirstSpringInitializrApplication.java Class

FirstSpringInitializrApplication.java is the launcher for Spring Boot:

    package com.mastering.spring;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure
    .SpringBootApplication;

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

FirstSpringInitializrApplicationTests Class

FirstSpringInitializrApplicationTests contains the basic context that can be used to start writing the tests as we start developing the application:

    package com.mastering.spring;
    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 FirstSpringInitializrApplicationTests {

      @Test
      public void contextLoads() {
      }
   }
 

A Quick Peek into Auto-Configuration


Auto-configuration is one of the most important features of Spring Boot. In this section, we will take a quick peek behind the scenes to understand how Spring Boot auto-configuration works.

Most of the Spring Boot auto-configuration magic comes from spring-boot-autoconfigure-{version}.jar. When we start any Spring Boot applications, a number of beans get auto-configured. How does this happen?

The following screenshot shows an extract from spring.factories from spring-boot-autoconfigure-{version}.jar. We have filtered out some of the configuration in the interest of space:

The preceding list of auto-configuration classes is run whenever a Spring Boot application is launched. Let's take a quick look at one of them:

org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.

Here's a small snippet:

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class WebMvcAutoConfiguration {

Some of the important points to note are as follows:

  • @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class }): This auto-configuration is enabled if any of the mentioned classes are in the classpath. When we add a web starter project, we bring in dependencies with all these classes. Hence, this auto-configuration will be enabled.

  • @ConditionalOnMissingBean(WebMvcConfigurationSupport.class): This auto-configuration is enabled only if the application does not explicitly declare a bean of the WebMvcConfigurationSupport.class class.

  • @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10): This specifies the precedence of this specific auto-configuration.

Let's look at another small snippet showing one of the methods from the same class:

    @Bean
    @ConditionalOnBean(ViewResolver.class)
    @ConditionalOnMissingBean(name = "viewResolver", 
    value = ContentNegotiatingViewResolver.class)
    public ContentNegotiatingViewResolver 
    viewResolver(BeanFactory beanFactory) {
      ContentNegotiatingViewResolver resolver = new 
      ContentNegotiatingViewResolver();
      resolver.setContentNegotiationManager
      (beanFactory.getBean(ContentNegotiationManager.class));
      resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
      return resolver;
     }

View resolvers are one of the beans configured by WebMvcAutoConfiguration class. The preceding snippet ensures that if a view resolver is not provided by the application, then Spring Boot auto-configures a default view resolver. Here are a few important points to note:

  • @ConditionalOnBean(ViewResolver.class): Create this bean if ViewResolver.class is on the classpath

  • @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class): Create this bean if there are no explicitly declared beans of the name viewResolver and of type ContentNegotiatingViewResolver.class

  • The rest of the method is configured in the view resolver

To summarize, all the auto-configuration logic is executed at the start of a Spring Boot application. If a specific class (from a specific dependency or starter project) is available on the classpath, then the auto configuration classes are executed. These auto-configuration classes look at what beans are already configured. Based on the existing beans, they enable the creation of the default beans.

 

Summary


Spring Boot makes the development of Spring-based applications easy. It enables us to create production-ready applications from day one of a project.

In this lesson, we covered the basics of Spring Boot and REST services. We discussed the different features of Spring Boot and created a few REST services with great tests. We understood what happens in the background with an in-depth look at auto-configuration.

In the next lesson, we will shift our attention toward adding more features to the REST services.

 

Assessments


  1. The _______ class can be used to bootstrap and launch a Spring application from a Java main method.

  2. Which of the following methods is used to append the local host URL and port to the URI to create a full URL?

    1. Private URL(String uri)

    2. private String create(String uri)

    3. private String CreateURL(String uri)

    4. private String createURL(String uri)

  3. State whether True or False: Tomcat server is launched on port 8080--Tomcat started on port(s): 8080 (http).

  4. Which of the following starter templates provides support for various unit testing frameworks such as JUnit, Mockito, and Hamcrest? These frameworks does the work of orchestrating the lock contention in Spring Boot.

    1. spring-boot-starter-test

    2. spring-boot-starter-testframe

    3. spring-boot-starter-unittest

    4. spring-boot-starter-testorchestration

  5. State whether True or False: The multipartResolver does not provide support to upload files in web applications.

About the Author

  • Ranga Rao Karanam

    Ranga Rao Karanam is the founder of in28Minutes, a company that trains 300,000 developers across the globe in relation to the cloud, microservices, Spring, Spring Boot, and Containers. He loves programming, and loves consulting for start-ups on developing scalable cloud applications and following modern development practices, such as BDD, continuous delivery, and DevOps. In his spare time, he loves hiking, cricket, and tennis. His ambition is to spend a year hiking the Himalayas.

    Browse publications by this author

Latest Reviews

(2 reviews total)
Great book useful for learners
too trivial, no good use cases and examples

Recommended For You

Book Title
Access this book, plus 7,500 other titles for FREE
Access now