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.
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
.
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:
- Kebab-case format (lower-case, hyphen-separated) must be used for prefixes. Examples of this are
demo
and demo-config
. - Property names can use kebab-case, camel-case, or snake-case. Examples of this are
user-Name
, firstName
, and telephone_number
. - 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.
Note
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 extendCustomEndpoint
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":[]
}
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
Note
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.