Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Microservices with Spring Boot 3 and Spring Cloud, Third Edition

You're reading from   Microservices with Spring Boot 3 and Spring Cloud, Third Edition Build resilient and scalable microservices using Spring Cloud, Istio, and Kubernetes

Arrow left icon
Product type Paperback
Published in Aug 2023
Publisher Packt
ISBN-13 9781805128694
Length 706 pages
Edition 3rd Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Magnus Larsson AB Magnus Larsson AB
Author Profile Icon Magnus Larsson AB
Magnus Larsson AB
Arrow right icon
View More author details
Toc

Table of Contents (26) Chapters Close

Preface 1. Introduction to Microservices 2. Introduction to Spring Boot FREE CHAPTER 3. Creating a Set of Cooperating Microservices 4. Deploying Our Microservices Using Docker 5. Adding an API Description Using OpenAPI 6. Adding Persistence 7. Developing Reactive Microservices 8. Introduction to Spring Cloud 9. Adding Service Discovery Using Netflix Eureka 10. Using Spring Cloud Gateway to Hide Microservices behind an Edge Server 11. Securing Access to APIs 12. Centralized Configuration 13. Improving Resilience Using Resilience4j 14. Understanding Distributed Tracing 15. Introduction to Kubernetes 16. Deploying Our Microservices to Kubernetes 17. Implementing Kubernetes Features to Simplify the System Landscape 18. Using a Service Mesh to Improve Observability and Management 19. Centralized Logging with the EFK Stack 20. Monitoring Microservices 21. Installation Instructions for macOS 22. Installation Instructions for Microsoft Windows with WSL 2 and Ubuntu 23. Native-Complied Java Microservices 24. Other Books You May Enjoy
25. Index

Spring Boot

Spring Boot, and the Spring Framework that Spring Boot is based on, is a great framework for developing microservices in Java.

When the Spring Framework v1.0 was released back in 2004, one of its main goals was to address the overly complex J2EE standard (short for Java 2 Platform, Enterprise Edition) with its infamous and heavyweight deployment descriptors. The Spring Framework provided a much more lightweight development model based on the concept of dependency injection. The Spring Framework also used far more lightweight XML configuration files compared to the deployment descriptors in J2EE.

To make things even worse with the J2EE standard, the heavyweight deployment descriptors actually came in two types:

  • Standard deployment descriptors, describing the configuration in a standardized way
  • Vendor-specific deployment descriptors, mapping the configuration to vendor-specific features in the vendor’s application server

In 2006, J2EE was renamed Java EE, short for Java Platform, Enterprise Edition. In 2017, Oracle submitted Java EE to the Eclipse Foundation. In February 2018, Java EE was renamed Jakarta EE. The new name, Jakarta EE, also affects the names of the Java packages defined by the standard, requiring developers to perform package renaming when upgrading to Jakarta EE, as described in the Migrating a Spring Boot 2 application section. Over the years, while the Spring Framework gained increasing popularity, the functionality in the Spring Framework grew significantly. Slowly, the burden of setting up a Spring application using the no-longer-so-lightweight XML configuration file became a problem.

In 2014, Spring Boot v1.0 was released, addressing these problems!

Convention over configuration and fat JAR files

Spring Boot targets the fast development of production-ready Spring applications by being strongly opinionated about how to set up both core modules from the Spring Framework and third-party products, such as libraries that are used for logging or connecting to a database. Spring Boot does that by applying a number of conventions by default, minimizing the need for configuration. Whenever required, each convention can be overridden by writing some configuration, case by case. This design pattern is known as convention over configuration and minimizes the need for initial configuration.

Configuration, when required, is, in my opinion, written best using Java and annotations. The good old XML-based configuration files can still be used, although they are significantly smaller than before Spring Boot was introduced.

Added to the usage of convention over configuration, Spring Boot also favors a runtime model based on a standalone JAR file, also known as a fat JAR file. Before Spring Boot, the most common way to run a Spring application was to deploy it as a WAR file on a Java EE web server, such as Apache Tomcat. WAR file deployment is still supported by Spring Boot.

A fat JAR file contains not only the classes and resource files of the application itself but also all the JAR files the application depends on. This means that the fat JAR file is the only JAR file required to run the application; that is, we only need to transfer one JAR file to an environment where we want to run the application instead of transferring the application’s JAR file along with all the JAR files the application depends on.

Starting a fat JAR requires no separately installed Java EE web server, such as Apache Tomcat. Instead, it can be started with a simple command such as java -jar app.jar, making it a perfect choice for running in a Docker container! If the Spring Boot application, for example, uses HTTP to expose a REST API, it will also contain an embedded web server.

Code examples for setting up a Spring Boot application

To better understand what this means, let’s look at some source code examples.

We will only look at some small fragments of code here to point out the main features. For a fully working example, you’ll have to wait until the next chapter!

The magic @SpringBootApplication annotation

The convention-based autoconfiguration mechanism can be initiated by annotating the application class (that is, the class that contains the static main method) with the @SpringBootApplication annotation. The following code shows this:

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

The following functionality will be provided by this annotation:

  • It enables component scanning, that is, looking for Spring components and configuration classes in the package of the application class and all its sub-packages.
  • The application class itself becomes a configuration class.
  • It enables autoconfiguration, where Spring Boot looks for JAR files in the classpath that it can configure automatically. For example, if you have Tomcat in the classpath, Spring Boot will automatically configure Tomcat as an embedded web server.

Component scanning

Let’s assume we have the following Spring component in the package of the application class (or in one of its sub-packages):

@Component
public class MyComponentImpl implements MyComponent { ...

Another component in the application can get this component automatically injected, also known as auto-wired, using the @Autowired annotation:

public class AnotherComponent {
  private final MyComponent myComponent;
  @Autowired
  public AnotherComponent(MyComponent myComponent) {
    this.myComponent = myComponent;
  }

I prefer using constructor injection (over field and setter injection) to keep the state in my components immutable. An immutable state is important if you want to be able to run the component in a multithreaded runtime environment.

If we want to use components that are declared in a package outside the application’s package, for example, a utility component shared by multiple Spring Boot applications, we can complement the @SpringBootApplication annotation in the application class with a @ComponentScan annotation:

package se.magnus.myapp;
@SpringBootApplication
@ComponentScan({"se.magnus.myapp","se.magnus.util" })
public class MyApplication {

We can now auto-wire components from the se.magnus.util package in the application code, for example, a utility component named MyUtility, as follows:

package se.magnus.util;
@Component
public class MyUtility { ...

This utility component can be auto-wired in an application component like so:

package se.magnus.myapp.services;
public class AnotherComponent {
 private final MyUtility myUtility;
 @Autowired
 public AnotherComponent(MyUtility myUtility) {
   this.myUtility = myUtility;
 }

Java-based configuration

If we want to override Spring Boot’s default configuration or we want to add our own configuration, we can simply annotate a class with @Configuration and it will be picked up by the component scanning mechanism we described previously.

For example, if we want to set up a filter in the processing of HTTP requests (handled by Spring WebFlux, which is described in the following section) that writes a log message at the beginning and the end of the processing, we can configure a log filter, as follows:

@Configuration
public class SubscriberApplication {
  @Bean
  public Filter logFilter() {
    CommonsRequestLoggingFilter filter = new 
        CommonsRequestLoggingFilter();
    filter.setIncludeQueryString(true);
    filter.setIncludePayload(true);
    filter.setMaxPayloadLength(5120);
    return filter;
  }

We can also place the configuration directly in the application class since the @SpringBootApplication annotation implies the @Configuration annotation.

That’s all for now about Spring Boot, but before moving to the next component, let’s see what is new in Spring Boot 3.0 and how to migrate a Spring Boot 2 application.

What’s new in Spring Boot 3.0

For the scope of this book, the most important new items in Spring Boot 3.0 are the following:

  • Observability

    Spring Boot 3.0 comes with improved support for observability, adding built-in support for distributed tracing to the already existing support for metrics and logging in previous Spring Boot releases. The new distributed tracing support is based on a new Observability API in Spring Framework v6.0 and a new module named Micrometer Tracing. Micrometer Tracing is based on Spring Cloud Sleuth, which is now deprecated. Chapter 14, Understand Distributed Tracing, covers how to use the new support for observability and distributed tracing.

  • Native compilation

    Spring Boot 3.0 also comes with support for compiling Spring Boot applications to native images, which are standalone executable files. A native-compiled Spring Boot application starts significantly faster and consumes less memory. Chapter 23, Native-Compiled Java Microservices, describes how to native compile microservices based on Spring Boot.

  • Virtual threads

    Finally, Spring Boot 3.0 comes with support for lightweight threads called virtual threads from the OpenJDK Project Loom. Virtual threads are expected to simplify the programming model for developing reactive non-blocking microservices, for example, compared to the programming model used in Project Reactor and various Spring components. Virtual threads are currently only available as a preview in Java 19. They also currently lack support for composability features, for example, required to build microservices that concurrently aggregate information from other microservices. Therefore, virtual threads will not be covered in this book. Chapter 7, Developing Reactive Microservices, covers how virtual threads can be implemented using Project Reactor and Spring WebFlux.

Migrating a Spring Boot 2 application

If you already have applications based on Spring Boot 2, you might be interested in understanding what it takes to migrate to Spring Boot 3.0. Here is a list of actions you need to take:

  1. Pivotal recommends first upgrading Spring Boot 2 applications to the latest v2.7.x release since their migration guide assumes you are on v2.7.
  2. Ensure you have Java 17 or later installed, both in your development and runtime environments. If your Spring Boot applications are deployed as Docker containers, you need to ensure that your company approves the usage of Docker images based on Java 17 or newer releases.
  3. Remove calls to deprecated methods in Spring Boot 2.x. All deprecated methods are removed in Spring Boot 3.0, so you must ensure that your application does not call any of these methods. To see exactly where calls are being made in your application, you can enable the lint:deprecation flag in the Java compiler using (assuming the use of Gradle):
    tasks.withType(JavaCompile) {
        options.compilerArgs += ['-Xlint:deprecation']
    }
    
  4. Rename all imports of javax packages that are now part of Jakarta EE to jakarta.
  5. For libraries that are not managed by Spring, you need to ensure that you are using versions that are Jakarta compliant, that is, using jakarta packages.
  6. For breaking changes and other important migration information, read through:

    https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide

    https://docs.spring.io/spring-security/reference/migration/index.html

  1. Ensure that you have end-to-end black-box tests that verify the functionality of your application. Run these tests before and after the migration to ensure that the application’s functionality has not been affected by the migration.

When migrating the source code of the previous edition of this book to Spring Boot 3.0, the most time-consuming part was figuring out how to handle breaking changes in the Spring Security configuration; see Chapter 11, Securing Access to APIs, for details. As an example, the following configuration of the authorization server in the previous edition needed to be updated:

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
  http
    .authorizeRequests(authorizeRequests -> authorizeRequests
      .antMatchers("/actuator/**").permitAll()

This configuration looks like the following with Spring Boot 3.0:

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
  http
    .authorizeHttpRequests(authorizeRequests -> authorizeRequests
      .requestMatchers("/actuator/**").permitAll()

The end-to-end test script, test-em-all.bash, that comes with each chapter turned out to be indispensable in verifying that the functionality was unaffected after the migration of each chapter.

Now that we have learned about Spring Boot, let’s talk about Spring WebFlux.

You have been reading a chapter from
Microservices with Spring Boot 3 and Spring Cloud, Third Edition - Third Edition
Published in: Aug 2023
Publisher: Packt
ISBN-13: 9781805128694
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image