This tutorial is a series of videos outlining
a number of steps to build a full Spring Boot application featuring a Kotlin service back
end, a Java client and a JavaFX user interface.
This second video will show how to create a Reactive Spring Java client that connects
to a REST service that’s streaming stock prices once a second.
We’re going to create a new project for this client, we want to keep the client and server
code completely separate as they should run completely independently. This project will have multiple modules in,
so we’ll start by creating an empty project. Let’s call the project stock-client. By default IntelliJ IDEA shows the modules
section of the Project Structure dialog when a new empty project is created. We’ll add a new module here, this will be
a Spring Boot module so we choose Spring Initializr on the left. We’ll use the latest version of Java, Java
13, for our SDK. We enter the group and artifact details here,
let’s call this module stock-client. We’ll enter a useful description for the module
so it’s clear what the purpose of this code is. We’ll keep the defaults of creating a Maven
project with Java as the language, but we’ll pick Java 11 as the version as this is the
current long term support version. We can optionally change the default package
structure if we wish.
We’ll use Spring Boot 2.2.0 RC1 because we’ll need some features from this version in later
videos of this tutorial. We need to select the Spring Reactive Web
starter, and we’re going to select Lombok too. The defaults for module name and location
are fine so we’ll keep them as they are. IntelliJ IDEA gets the project from Spring
Initializr and sets up the IDE project appropriately. For example, it’s recognised the project is
a maven project.
In the standard project structure we can see the StockClientApplication that Spring Initializr
has created for us. We don’t need this for this module, as this
module is going to be a library that other applications use, it’s not going to be an
application in its own right. Instead, let’s create a class WebClientStockClient,
this is going to use Spring’s WebClient to connect to the stock prices service.
One way to drive out the requirements for our client, and to see if it actually works,
is to develop it in a test driven way. Using Ctrl+Shift+T, or Cmd+Shift+T, we can
navigate to the test for a class. We don’t have one yet for this class so let’s
create one. We’re going to use JUnit 5, but IntelliJ IDEA
offers a range of testing frameworks to choose from.
This is actually going to be an end-to-end test, not a unit test, so let’s rename the
class using Shift+F6, so that it more accurately represents the type of test. Let’s generate a test method using Alt + Insert
or Cmd N. This is not going to be a perfect example
of test driven development, as we’re going to just create a single test which looks at
only the best case, sometimes called the happy path.
We need to create an instance of our WebClientStockClient in order to test it, let’s put this into a
variable so we can use it.
One of the things we can do with test driven development is to code against the API we
want, instead of testing something we’ve already created. Let’s say we want our client to have a method
called pricesFor, which takes some String that represents the symbol of the stock we
want the prices for. We can use Alt and Enter to get IntelliJ IDEA
to create this method on WebClientStockClient, with the expected signature. We want this method to return a Flux of StockPrice
objects. The simplest way to create a method that compiles
so we can run our test against it, is to get this method to return an empty Flux. We will build up the real logic later. Now we need to create our StockPrice class. It’s easiest to get IntelliJ IDEA to create
the class using Alt + Enter. We can pick which package we want it to go
into, let’s just put it into the same package.
Here’s where we’re going to use Lombok. Using Lombok’s @Data annotation, we can create
a data class similar to our Kotlin data class in the first video of this tutorial. By annotating this with @Data, we only need
to define the properties of this class using fields, the getters, setters, equals, hashCode,
and toString methods are all provided by Lombok. Use the Lombok IntelliJ IDEA plugin to get
code completion and other useful features when working with Lombok.
This StockPrice will have a String symbol, a Double price and a LocalDateTime time, the
same as the Kotlin class on the service side. We’ll add an AllArgsConstructor and a NoArgsConstructor
via Lombok, these are needed for our code and for JSON serialisation.
If we go back to our client we can see everything is compiling. Let’s navigate back to our test and finish
Now we have a Flux of prices, we need to check this meets our expectations. If this was connected to a real working service,
we’d expect the prices Flux not to be null. We’d also expect that if we take five items
from the Flux that there are actually some items there. We have to use “block” in this case to wait
for our prices.
When we run this test we see that it fails. It fails because the Flux has zero elements
in it, because that’s what we hardcoded into the client.
Let’s go back to the client and make the changes needed to connect it to the real service.
First, we want to use a WebClient to connect to the service. We’ll create this as a field, and add a constructor
parameter so that Spring automatically wires this in for us.
Now we use the web client to make a get request. We give it the URI of our service, we know
this was as we put the URI into the browser to test the service when we created it back
in the first video. We need to use the given symbol in this URI. Then we need to call retrieve(). We need to say how to turn the response of
this call into a Flux of some type, so we use bodyToFlux and give it our data class,
These are the very basic requirements to get a reactive stream from a GET call, but we
can also define things like the retry and back off strategy, remember that understanding
the flow of data from publisher to consumer is an important part of creating a successful
reactive application. We can also define what to do when specific
Exceptions are thrown. As an example, we can say that when we see
an IOException we want to log it. We’ll use the Log4j2 annotation from Lombok
to give us access to the log, and log an error. This is not the most robust way to handle
errors, this simply shows that we can consider Exceptions as a first class concern in our
Going back to our test, we see we now need to give our client a WebClient. Let’s create this as a field in the test. Using Smart Completion, Ctrl Shift Space,
IntelliJ IDEA will even suggest the full call to the builder that can create a WebClient
instance. If we needed to we could use the builder to
configure our WebClient even further, but in our case we can use the default settings.
We want to run our integration test to see if our changes work, but first we need to
make sure the service is up and running, otherwise we won’t be able to connect to it. We need to go back to the StockServiceApplication
we created in the first video and start this.
Once this is running, we can run the client test. This time, the test goes green. And if we look at the output, we can see we’re
decoding StockPrice objects with the symbol that we used in the test, random prices and
This is not the most thorough test, so let’s add a bit more detail to our assertions to
make sure the client really is doing what we expect. Let’s change the assertion to require five
prices when we take five prices, and let’s make sure that the symbol of one of the stock
prices is what we expect.
Testing reactive applications is a skill in its own right, and there are much better ways
to do it than we’ve shown in this simple example. However we have successfully used an integration
test to drive the API and functionality of our Stock Prices client. This client connects to an endpoint that emits
server-sent events and returns a Flux of StockPrice objects that could be consumed by another
service. We’ll show how to do this in later videos
in this tutorial.
Thanks for watching!