Book Image

Spring Boot 2.0 Projects

By : Mohamed Shazin Sadakath
4 (1)
Book Image

Spring Boot 2.0 Projects

4 (1)
By: Mohamed Shazin Sadakath

Overview of this book

Spring Boot is a lightweight framework that provides a set of tools to create production-grade applications and services. Spring Boot 2.0 Projects is a comprehensive project-based guide for those who are new to Spring, that will get you up to speed with building real-world projects. Complete with clear step-by-step instructions, these easy-to-follow tutorials demonstrate best practices and key insights into building efficient applications with Spring Boot. The book starts off by teaching you how to develop a web application using Spring Boot, followed by giving you an understanding of creating a Spring Boot-based simple blog management system that uses Elasticsearch as the data store. Next, you’ll build a RESTful web services application using Kotlin and the Spring WebFlux framework - a new framework that enables you to create reactive applications in a functional way. Toward the last few chapters, you will build a taxi-hailing API with reactive microservices using Spring Boot, in addition to developing a Twitter clone with the help of a Spring Boot backend. To build on your knowledge further, you’ll also learn how to construct an asynchronous email formatter. By the end of this book, you’ll have a firm foundation in Spring programming and understand how to build powerful, engaging applications in Java using the Spring Boot framework.
Table of Contents (12 chapters)

Changes since Spring Boot 1.x

The last released version of Spring Boot 1.x was 1.5.10.RELEASE, after which Spring Boot 2.0 was released in early 2018. As Spring Boot 2.0 is a major release it has JVM level, platform level, application programming interface (API) level, and dependencies level changes, which must be taken into account when developing applications with Spring Boot 2.0.

The major changes from Spring Boot 1.x to Spring Boot 2.0 are listed as follows:

  • Java 8 is the baseline and Java 9 is supported: This means the minimum JVM version on which a Spring Boot 2.0 application can be run is now Java 8 because the framework is modified to use many features introduced in Java 8. Furthermore, Spring Boot 2.0 is fully supported to run on Java 9, which means all the dependency JARs shipped will have module descriptors to support the Java 9 module system.
  • Third-party libraries upgraded: Spring Boot 2.0 requires Spring Framework 5.0 along with Tomcat 8.5, Flyway 5, Hibernate 5.2, and Thymeleaf 3.
  • Reactive Spring supported: Supports the development of functional, non-blocking, and asynchronous applications out of the box. Reactive Spring will be explained and used extensively in upcoming chapters.
  • Micrometer Framework introduced for metrics: Uses Micrometer instead of its own API for metrics. A micrometer is a third-party framework that allows dimensional metrics.
  • Spring Boot Actuator changed: Spring Boot Actuator endpoints are now placed inside the context path /actuator instead of being mapped directly to root, to avoid path collisions. Additionally, a new set of endpoint annotations have been introduced to write custom endpoints for Spring Boot Actuator.
  • Configuration property binding: Improved relaxed binding of properties, property origins, converter support, and a new Binder API.
  • Kotlin language supported: Supports Kotlin, a new concise and interoperable programming language introduced by IDEA. Kotlin will be explained in detail in Chapter 04, Introduction to Kotlin.
  • HikariCP shipped out of the box instead of Tomcat Connection Pool: HikariCP is the most efficient, high-performing database connection pool available for the JVM and it is shipped by default with Spring Boot 2.0.
  • A new way to dynamically register Spring Bean with ApplicationContextInitializer: Adds to previous methods of registering a Spring Bean by providing the means to define it in an XML file, annotate @Bean on a method that returns an object, annotate with @Component, @Service, @Repository annotations, and so on. Spring Framework 5 has introduced ApplicationContextInitializer, which can do dynamic registering.
  • HTTP/2 supports out of the box: HTTP/2 is the latest version of the widely used Hypertext Transfer Protocol (HTTP), which has a lot of performance gains when compared to older versions.
  • Newly added event ApplicationStartedEvent: An ApplicationStartedEvent will be sent right after the application context is refreshed but before any command line runners run. ApplicationReadyEvent will, however, be sent right after the application context is refreshed and any command-line runners run. This means the application is in a ready state to accept and process requests.

These are the most notable changes, but there are so many more, as with any major release. Other changes can be seen in the Spring Boot 2.0 release notes, found at https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes.

Let's have a look at some of the notable features of Spring Boot 2.0 with some hands-on examples.

Registering a Spring Bean using ApplicationContextInitializer

Spring Boot allows Builder to create customized Spring Boot application bootstrapping with a tool called SpringApplicationBuilder. This can be used as follows to customize the Spring Boot application and register a Spring Bean dynamically:

public static void main(String[] args) {
new SpringApplicationBuilder(SpringBoot2IntroApplication.class)
.bannerMode(Banner.Mode.OFF)
.initializers((GenericApplicationContext
genericApplicationContext) -> {
genericApplicationContext.registerBean
("internet",
InternetHealthIndicator.class);
})

         .run(args);
}

In this code, a new instance of SpringApplicationBuilder is instantiated with a configuration class. By invoking the bannerMode(Banner.Mode.OFF) method, the banner shown in the console at the Spring Boot Bootstrap is switched off.

By invoking the initializers() method with a lambda function (learn about lambda functions in the reference documentation at https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html) for ApplicationContextInitializer, the GenericApplicationContext.registerBean method is used to register a Spring Bean called internet and with class type InternetHealthIndicator.

Configuration property binding

Configuration properties are a great feature of Spring Boot for reading properties with type safety. This section will explain the concept with the following DemoApplicationProperties class file:

@ConfigurationProperties(prefix = "demo")
public class DemoApplicationProperties {

private Integer number;

private String username;

private String telephoneNumber;

private List<String> emailAddresses =
Arrays.asList("[email protected]");

private String firstName;

private String lastName;

private Duration workingTime;

// Getters and Setters
}

The application.yml has the following configuration properties:

demo:
number: 10
user-Name: shazin
firstName: Shazin
lAsTNamE: Sadakath
telephone_number: "0094777329939"
workingTime: 8h
EMAILADDRESSES:
- [email protected]
- [email protected]
addresses:
- number: "B 22 2/3"
city: Colombo
street: "Ramya Place"
country: "Sri Lanka"
zipCode: "01000"
- number: "123"
city: Kandy
street: "Dalada Weediya"
country: "Sri Lanka"
zipCode: "01332"

For configuration properties, an application.properties file also can be used over an application.yml file. Lately, YML files are becoming famous among developers because they can provide a structure with indentations, the ability to use collections, and so on. Spring Boot supports both at the moment.

New property binding API

Spring Boot 2.0 introduces a new binding API for configuration properties. The most notable change in Spring Boot 2.0 related to configuration property binding is the introduction of a new binding API with the following Address Plain Old Java Object (POJO):

public class Address {
private String number;
private String street;
private String city;
private String country;
private String zipCode;
// Getters, Setters, Equals, Hashcode
}

The following Binder fluent API can be used to map properties directly into the Address POJO.

This code can be written inside any initializing code such as CommandLineRunner, ApplicationRunner, and so on. In the application this code is available inside the SpringBoot2IntroApplication.runner method:

List<Address> addresses = Binder.get(environment)
.bind("demo.addresses", Bindable.listOf(Address.class))
.orElseThrow(IllegalStateException::new);

The preceding code will create a Binder instance from the given Environment instance and bind the property for a list of Address classes. If it fails to bind the property then it will throw IllegalStateException.

Property origin

Another notable addition to configuration property binding is exposing the origins of a property. This is a useful feature because many developers have struggled in the past because they had configured the wrong file and ran an application to just see unexpected results. Now, the origin of a property is shown along with the file, line number, and column number:

"demo.user-Name": {  
"value":"shazin",
"origin":"class path resource [application.yml]:5:14"
}

Tightened rules for governing relaxed property binding

Relaxed property binding rules have the following changes:

  1. Kebab-case format (lower-case, hyphen-separated) must be used for prefixes. Examples of this are demo and demo-config.
  2. Property names can use kebab-case, camel-case, or snake-case. Examples of this are user-Name, firstName, and telephone_number.
  3. The upper case underscore format that is usually used for environment variables should be followed, where the underscore should only be used to separate parts of the key. The upper case underscore format is usually used for environment variables. The underscore is used to separate parts of the key. An example of this would be DEMO_ENV_1.

The complete set of rules for relaxed bindings can be seen in the Spring Boot documentation at https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding.

Environment variables with indices

Furthermore, environment variables with indices can be mapped to names with array syntax and indices, shown as follows:

DEMO_ENV_1 = demo.env[1]
DEMO_ENV_1_2 = demo.env[1][2]

So, the DEMO_ENV_1 environment variable can be read in the application as follows:

System.out.printf("Demo Env 1 : %s\n", environment.getProperty("demo.env[1]"));

Direct binding of property type java.time.Duration in the ISO-8601 form

Another notable change is the ability to specify time duration in days (d), hours (h), minutes (m), seconds (s), milliseconds (ms), and nanoseconds (ns), which will be correctly mapped to a java.time.Duration object in the configuration property. In the example workingTime: 8h, the property will be mapped to the java.time.Duration workingTime property correctly. Values such as 8m, 8s, 8d can also be specified to define duration.

Custom endpoints for Spring Boot Actuator using annotations

In Spring Boot 1.x, in order to write a custom endpoint for Spring Boot Actuator, AbstractEndpoint had to be extended and its invoke method has been overridden with custom logic. Spring Boot Actuator is a production-ready feature for monitoring and managing a Spring Boot application using HTTP endpoints or JMX. Auditing metrics such as health could also be gathered using this feature. Finally, it had to be injected into the Spring Context as a Bean. This endpoint was technologically agnostic, in the sense that it could be invoked using JMX as well as with web requests.

If a custom behavior was required for a particular technology, such as JMX or the web, then AbstractEndpointMvcAdapter or EndpointMBean had to be extended respectively and had to be injected as a Spring Bean. This is cumbersome, and Spring Boot 2.0 has introduced technology-specific annotations such as @Endpoint, @WebEndpoint, and @JmxEndpoint; technology-independent operation annotations such as @ReadOperation, @WriteOperation, and @DeleteOperation; and technology-specific extension annotations such as @EndpointWebExtension and @EndpointJmxExtension, to ease this process.

By default, only the /info and /health endpoints are exposed. The management.endpoints.web.exposure.include=* property must be set to expose other endpoints, including custom ones.

Exposing a custom Spring Boot Actuator endpoint

The @ReadOperation annotation will expose the Getter for the custom endpoint and the @WriteOperation will expose the Setter for the custom endpoint. The endpoint will be mapped under the http://<host>:<port>/actuator/custom URL (the default host is localhost, and the default port is 8080 unless configured otherwise) and also exposed as a JMX Management bean:

@Component
@Endpoint(id = "custom")
public class CustomEndpoint {

private final static Logger LOGGER =
LoggerFactory.getLogger(CustomEndpoint.class);

@ReadOperation
public String get() {
return "Custom Endpoint";
}

@WriteOperation
public void set(String message) {
LOGGER.info("Custom Endpoint Message {}", message);
}
}

Extending a custom endpoint with a specialized implementation for the web

The following extension class, which uses @EndpointWebExtension to extend CustomEndpoint for custom behavior for web technology and for JMX technology, will not be changed:

@Component
@EndpointWebExtension(endpoint = CustomEndpoint.class)
public class CustomEndpointWebExtension {
...

@ReadOperation
public WebEndpointResponse<String> getWeb() {
...
return new WebEndpointResponse<>("Custom Web
Extension Hello, World!"
, 200);
}
}

The @EndpointWebExtension annotation will make the CustomEndpointWebExtension a web extension for CustomEndpoint with its endpoint property. The method with the @ReadOperation annotation will be the overridden Getter. Accessing http://<host>:<port>/actuator/custom (the default host is localhost, and the default port is 8080 unless configured otherwise) and a URL using a browser will prompt for the username (specify sysuser) and password (specify password) and when logged in will return the following:

Custom Web Extension Hello, World!

Connecting to a custom endpoint using monitoring and management tools

Running the Spring Boot application with the following VM arguments will enable it to be connected using jconsole remotely, and the exposed CustomEndpoint JMX Bean can be accessed using monitoring and management tools such as jconsole, which is shipped with the JDK installation:

-Djavax.management.builder.initial=      
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8855
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

Run jconsole with the following command:

$ jconsole

Making a remote process connected with it to <host>:8855 will list all the MBeans from the Spring Boot application under MBeans tab. When the Custom.get operation is executed from jconsole it will show the return from CustomEndpoint.get as expected, as shown in the following screenshot:

Custom metrics using Micrometer

With the introduction of Micrometer to Spring Boot Actuator in Spring Boot 2.0, metrics can be customized easily. The following code snippet from CustomEndpointWebExtension shows how to make use of io.micrometer.core.instrument.MeterRegistry to maintain a counter with the name custom.endpoint.calls, which will be incremented every time CustomEndpointWebExtension is invoked:

public static final String CUSTOM_ENDPOINT_CALLS = "custom.endpoint.calls";

private final MeterRegistry meterRegistry;

public CustomEndpointWebExtension(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}

@ReadOperation
public WebEndpointResponse<String> getWeb() {
meterRegistry.counter(CUSTOM_ENDPOINT_CALLS).increment();
return new WebEndpointResponse<>("Custom Web Extension Hello, World!", 200);
}

The preceding code injects MeterRegistry from the Micrometer Framework, which is used to create and retrieve a counter named custom.endpoint.calls and increment it during each read operation of the custom web endpoint extension.

This metric will be available under the http://<host>:<port>/actuator/metrics/custom.endpoint.calls URL, which will show a result similar to the following:

{  
"name":"custom.endpoint.calls",
"measurements":[
{
"statistic":"COUNT",
"value":3.0
}
],
"availableTags":[ ]
}

Custom health indicator

Spring Boot Actuator's health endpoint is really helpful for checking the status of Spring Boot application and dependent systems such as databases, message queues, and so on. Spring Boot ships out of the box with many standard HealthIndicator implementations such as DiskSpaceHealthIndicator, DataSourceHealthIndicator, and MailHealthIndicator, which can all be used in Spring Boot applications with Spring Boot Actuator. Furthermore, custom health indicators can also be implemented if required:

public class InternetHealthIndicator implements HealthIndicator {

private static final Logger LOGGER = LoggerFactory.getLogger(InternetHealthIndicator.class);

public static final String UNIVERAL_INTERNET_CONNECTIVITY_CHECKING_URL = "https://www.google.com";

private final RestTemplate restTemplate;

public InternetHealthIndicator(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}

@Override
public Health health() {
try {
ResponseEntity<String> response = restTemplate.getForEntity(UNIVERAL_INTERNET_CONNECTIVITY_CHECKING_URL, String.class);
LOGGER.info("Internet Health Response Code {}",
response.getStatusCode());
if (response.getStatusCode().is2xxSuccessful()) {
return Health.up().build();
}
} catch (Exception e) {
LOGGER.error("Error occurred while checking
internet connectivity"
, e);
return Health.down(e).build();
}

return Health.down().build();
}
}

The preceding InternetHealthIndicator is intended to show the status of internet connectivity from within Spring Boot applications to the outside world. It will send a request to www.google.com to check whether it sends a successful HTTP response code, and based on that the health status of this indicator will be set to up or down. This was injected as a Spring Bean using an ApplicationContextInitializer earlier. Invoking the http://<host>:<port>/actuator/health URL will return the internet status as in the following:

{  
"status":"UP",
"details":{
"internet":{
"status":"UP"
},
"diskSpace":{
"status":"UP",
"details":{
"total":399268376576,
"free":232285409280,
"threshold":10485760
}
}
}
}

Using the HTTP/2 protocol

HTTP was invented by Tim Berners-Lee in 1989 while he was working at CERN. It was designed as a way to share scientific findings among coworkers and is almost three decades old. When HTTP was invented, it was never intended to be the backbone of today's low-latency, high-traffic web, used by millions if not billions of people. So HTTP 1-and HTTP 1.1-based web applications had to have a lot of workarounds to cater to the high demands of the modern web. The following are some of those workarounds:

  • Concurrent resources are download by the browser since HTTP 1.x can download only one resource at a time, and Domain Sharding is used to tackle limitations on the maximum number of connections per domain
  • Combining multiple resources such as CSS/Javascript files together with complex server-side logic and downloading them all in one go
  • Multiple image sprites in a single resource to reduce the number of image file downloads
  • Inlining static resources in an HTML file itself

But HTTP/2 is designed from the ground up to tackle these pain points. Compared to HTTP 1.x, HTTP/2 doesn't use text to communicate between the client and server. It uses binary data frames, which makes it much more efficient and reduces the text-to-binary and binary-to-text conversion overhead in the servers. Furthermore, it has the following features introduced:

  • HTTP/2 multiplexing: This multiplexing feature allows opening one connection to a server and downloading multiple resources using that connection:
  • HTTP/2 push: This will send resources to clients even before resources are requested:

  • HTTP/2 header compression: Eliminates the repeating of the same headers across multiple requests by maintaining an HTTP header table, thus reducing request bandwidth

Spring Boot 2 supports HTTP/2 out of the box for server Undertow, and, with minor dependencies at https://docs.spring.io/spring-boot/docs/2.0.x-SNAPSHOT/reference/htmlsingle/#howto-configure-http2, also supports Tomcat and Jetty servers. But Spring Boot 2 doesn't support the clear text version of HTTP/2, so Secure Socket Layer (SSL) is a must. It requires the following dependencies in pom.xml :

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

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

The configuration properties in application.yml configure both SSL and HTTP/2:

 server:
port: 8443
ssl:
key-store: keystore.p12
key-store-password: sslcert123
keyStoreType: PKCS12
keyAlias: sslcert

http2:
enabled: true

In this configuration SSL key-store, key-store-password, keyStoreType, and keyAlias are configured along with the port for HTTPs. The key was generated using keytool, which is a utility shipped with the JDK release with the following command, and fills in the necessary details when prompted by the utility tool:

$ keytool -genkey -alias sslcert -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
This key-store is not recommended for production as it is not generated by a Trusted Certificate Authority. A proper SSL Certificate must be used in production.

Now when the https://<localhost>:8443/actuator/custom URL is accessed it will be served over HTTP/2.

Securing applications with Spring Security

Spring Boot 2.0 has introduced updated support for Spring Security with Spring Framework 5.0 and Reactive support for Spring Security, providing simplified default configurations and ease of customization for Spring Security. As opposed to having multiple auto-configurations for Spring Security, Spring Boot 2.0 has introduced a single behavior that can be overridden easily and can be customized easily with a WebSecurityConfigurerAdapter such as the following:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.authorizeRequests()
.requestMatchers(EndpointRequest.to("info", "health")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("SYSTEM")
.antMatchers("/**").hasRole("USER");

}

@Override
protected void configure(AuthenticationManagerBuilder
auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(new
MessageDigestPasswordEncoder("SHA-256"))
.withUser("user")
.password("5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8")
.roles("USER")
.and()
.withUser("sysuser")
.password("5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8")
.roles("SYSTEM");
}

}

One thing to note here is the introduction of the EndpointRequest helper class, which makes it easier to protect endpoints.