February 8, 2022

How to Build Microservices in Spring Boot in 15 Minutes

Table of Contents

Spring Boot streamlines building microservices by simplifying development and deployment. It supports rapid iteration, scalability, and robust integration, helping developers focus on innovation rather than infrastructure.

Speed up development cycles, reduce release risk, and focus your team on DevOps best practices that create maximum impact. Get a personalized demo today!

There are a lot of parts to this project, so let’s take a closer look at them before you get started.

The API gateway will be the publicly accessible service. This is the service that will act as the doorway into the microservice. In this example application, it will return processed weather data, such as temperatures for zips and city names. The weather data will be retrieved from a private service in the microservice network, which we are calling the weather resource server.

The API gateway will not have the URI for the private weather resource server hardcoded into it. Instead, it will look up the address on a Eureka discovery server. In our small, single-instance microservice there’s no actual need for a discovery server but in a larger framework, the Eureka server allows server instances to register themselves as they are added to the network and de-registered as they go down. It’s like a phone book for the microservice network (for those of us who remember phone books), or a contacts list (for anyone born after about 1985).

The discovery server has two main benefits:

  1. It decouples service providers from clients without requiring DNS
  2. It enables client-side load-balancing

There’s a nice tutorial on the Spring website that covers load balancing with Spring Cloud and the reasons you might want to use client-side load-balancing over a traditional DNS load balancer. This tutorial won’t cover actually implementing load balancing. However, it will cover the benefits of client-side load balancing. Client-side load balancing is smarter because it has access to application state and because it can avoid the dogpiling effect that DNS caching can result in with traditional DNS load balancers.

Notice that the weather resource server will not generate the weather information itself. Instead, it will retrieve it from the OpenWeatherMap API.

The weather resource server will also implement two versions of the API, and you’ll see how to use Split by Harness’s feature flag service to dynamically enable and disable the V2 API in real-time without having to redeploy code.

You’ll also see how to implement HTTP Basic authentication on the API gateway using Spring Security 5.

Requirements

Before you get started, you’ll need to make sure you have a few tools installed.

Split by Harness: Sign up for a free Split account if you don’t already have one. This is how you’ll implement the feature flags.

HTTPie: This is a powerful command-line HTTP request utility. Install it according to the docs on their site.

Java 11: This project uses Java 11. OpenJDK 11 will work just as well. Instructions are found on the OpenJDK website. OpenJDK can also be installed using Homebrew. Alternatively, SDKMAN is another excellent option for installing and managing Java versions.

OpenWeatherMap: for demonstration purposes in this tutorial, you’ll need a free OpenWeatherMap API account. Sign up here.

Create the Weather Resource Server

The first component in the microservice network you’re going to create is the weather resource server. This is the private service that sits within the network that will be publicly accessed via the API gateway service. You’ll create the API gateway service in a moment.

Step 1 – For this service, you’re going to need your OpenWeatherMap API key. Make sure you’ve already signed up for a free account. You can find the API key by signing in to your OpenWeatherMap account and going to the API key page or by selecting My API Keys from the account drop-down.

Create a parent directory for the three Spring Boot projects your going to make, maybe something like split-spring-cloud-microservice.

Use the Spring Initializr REST API to bootstrap your project by running the following command from the project parent directory.

This creates a Spring Boot project configured to use Maven as the build system and dependency manager with Java 11 and Spring Boot version 2.6.2. The dependencies you’re including are:

  • actuator: exposes some pre-defined endpoints for metadata about the application
  • web: Spring web framework that includes the MVC/non-reactive web tools necessary to create a REST API
  • cloud-eureka: service discovery framework that the API gateway will use to look up the URI for the weather resource server
  • cloud-feign: “declarative web service client” (as described in the Spring docs) that simplifies and structures how external web services are called
  • lombok: helper that includes annotations for automatically generating ceremony code such as constructors, getters, and setters
  • devtools: a Spring Boot helper for app development that includes things like live reload and automatic restart

Step 2 – From within the weather-service directory, you need to create a few files and configure the application.

Open src/main/resources/application.properties and add the following:

Make sure you replace {yourOpenWeatherApiKey} with your actual API key from the OpenWeatherMap website.

You’re setting the server port to 8090 so that the default ports on the services do not clash.

The spring.application.name value will be the value that the service is registered under with the Eureka server. This value essentially takes the place of the service URI and is the value that the gateway will use to look up the weather service URI. As mentioned previously, this abstraction in the context of the microservice allows a lot of flexibility for service redundancy, load balancing, and emergency fail-over.

Update the main application file.

com/example/weatherservice/WeatherServiceApplication.java

The only change in this file is the addition of the @EnableFeignClients annotation. This lets Spring Boot know that the service will use Feign clients and to look for the @FeignClient annotation on interfaces.

Now you need a class to hold the response from the OpenWeatherMap API. Spring Boot will use Jackson deserialization to automatically map the JSON response from the OpenWeatherMap API to the Java objects described in this file.

com/example/weatherservice/OpenWeatherApiResponse.java

The specific OpenWeatherMap API you’ll be calling returns current weather data. You can take a look at the documentation for it on their website. You’ll notice that the API response actually returns a lot more data than is captured here (there just wasn’t any reason to try and deserialize and map all of the data from the returned JSON).

Step 3 – Create a file to hold the V1 version of the weather resource server.

com/example/weatherservice/WeatherRestControllerV1.java

The interface at the top of this file is the Feign client that describes to Spring Boot how you want to make calls to the OpenWeatherMap API. That client is then injected into the WeatherRestControllerV1, where it is used to retrieve weather information that is exposed by its getWeatherByZip(zip), which is what the API gateway will call.

The version 1 API has only one method that returns the weather by zip, which is mapped to api/v1/weather and expects the zip as a query param.

Step 4 – Create a controller for the V2 version of the weather resource server.

com/example/weatherservice/WeatherRestControllerV2.java

The V2 API adds the ability to retrieve weather information based on city, state, and country. Notice that the private, weather resource server will simultaneously expose both API versions. When you implement the gateway, you will use feature flags and Split by Harness to control access to the V2 API, simulating a test rollout.

Create the API Gateway and the Eureka Server

Step 5 – Again use Spring Initializr to download a pre-configured starter. Run the command below from a Bash shell within the parent directory of the tutorial project (not within the weather resource server project directory).

The dependencies and other parameters here are very similar, so I won’t explain them.

Add the following line to the application.properties file.

src/main/resources/application.properties

Update the ApiGatewayApplication class to the following:

src/main/java/com/example/apigateway/ApiGatewayApplication.java

This file has the Feign client interface for accessing the V1 weather resource server as well as a public method accessible at the endpoint /temperature/zip/{zip}. This does not yet have any V2 methods or endpoints. In the next section, you’ll see how to add the V2 API methods and do a controlled rollout with Split by Harness and feature flags.

Step 6 – Create the data model file for the weather data.

src/main/java/com/example/apigateway/OpenWeatherApiResponse.java

This is just a duplicate of the same file in the weather resource server.

Finally, the last piece, you need to create the Eureka discovery server. Again, from the parent directory, run the following command to download a starter project.

The only dependency here is cloud-eureka-server, which brings in the necessary dependencies to creating a discovery service.

Update application.properties to the following.

src/main/resources/application.properties

Open DiscoveryServiceApplication and make sure it matches the following. All you are doing is adding the @EnableEurekaServer annotation.

That’s all you have to do to create the Eureka discovery server!

Test the Weather App Microservice

Step 7 – Start the projects using ./mvnw spring-boot:run from a Bash shell. Start the Eureka server first, then the private weather resource server, and finally the API gateway. If you start things in the wrong order, the dead will definitely rise from their graves and take over the world, so don’t mix it up.

Once all three services are running, open the Spring Eureka dashboard at http://localhost:8761/.

You should see something like this, with two service instances running.

Split - How to Build Microservices in Spring Boot in 15 Minutes - 2

The ports for the different services you have running are:

  • 8080: the public weather service port, the API gateway
  • 8090: the private weather resource server within the microservice network
  • 8761: the Eureka discovery server port

You can test the microservice by making the following request from a new Bash shell.

You could also make a request to the private weather resource service. This is the JSON data that the private API is returning from the OpenWeatherMaps service.

This corresponds to the following request being made to the OpenWeatherMaps API.

Use Feature Management & Experimentation  to Implement a Controlled Rollout

Step 8 – Now you’re going to use Harness Feature Management & Experimentation's implementation of feature flags. Feature flags are a way to control code dynamically at runtime. You can think of them as dynamic variables whose state can be controlled in real-time, both manually and automatically, based on a large number of configurable parameters on the Feature Management dashboard. In this example, you’ll use a feature flag to simulate a controlled roll-out of the V2 API, but there are lots of potential uses cases for feature flags.

They’re applicable any time you want to segment application behavior based on a user identity or any other application parameter. You could, for example, use it to give some users access to premium features, to selectively test features with certain users, or to expose admin and debug features only to certain accounts.

Before you get started, let’s go over a little terminology and some basic concepts that Feature Management uses. The feature flag is a String key that lives on the Feature Management servers whose value can be looked up using the Feature Management & Experimentation SDK. As you’ll see, Harness does some fancy caching so that you don’t have to worry about network calls slowing down your code when this lookup happens.

The value a feature flag takes is a treatment. Treatments are strings. Feature flags can have any number of treatments from two to dozens. The treatment, or value, that a feature flag has at any given point is determined by rules configured on the Feature Management dashboard. These rules are called targeting rules. There are a lot of options, and changes made to a feature flag are propagated in real-time to your application. Thus they are like having dynamic, real-time, configurable switched in your application code.

One common way of determining a feature flag's treatment is by using a segment of the user population. A segment is a sub-group of your users. This could be your testers. This could be your freemium users. This could be the newest users. This could be everyone in Austin, Texas. As you’ll see, there’s a ton of flexibility in how the system is implemented. But the idea is, for example, that you can create groups of users, or segments, for whom the feature flag has one treatment while the rest default to another treatment.

In the next section of the tutorial, you’re going to see how you can use Feature Management & Experimentation to selectively expose the V2 API, which you’ll implement below, to a segment of users. You’ll see how this can be done in real-time, without redeploying any code, and how you can either roll the deployment back or expand the deployment, also dynamically without code changes, depending on how the rollout goes.

Create a Feature Flag

Step 9 – Log into your Feature Management & Experimentation dashboard.

From the left-menu, under TARGET, click Feature Flag. Click the blue Create Feature Flag button.

Name the feature flagv2-deployment. Change the traffic type to user.

Click Create.

You just created the feature flag. Now you need to define the different treatments (or values) the feature flag can take and some targeting rules to determine under what conditions each state is active.

In this section of the tutorial, you’re going to use two users to simulate a test deployment. user1 will have the new V2 API activated while user2 will only see the old V1 API. While here you are only using two users, it’s very simple to expand this to a list of users (a segment) that is either imported via a CSV file or dynamically assigned based on user attributes.

On the v2-deploymentfeature flag panel, click the Add Rules button.

This creates a new set of targeting rules for your feature flag. It will open the targeting rules panel. Each environment has a different set of targeting rules. The environment defaults to Staging-Default, which is a test environment. There is also a production environment called Prod-Default pre-configured. Environments can be added and configured in the Environments section of the main menu (on the left of the dash).

The first header, Define treatments, is where the treatments are defined. The default treatment simulates a boolean on/off system. Remember, though, that these are just string values and can be given any name and can take on any arbitrary meaning in your application. Further, you can have more than two treatments per feature flag.

Step 10 – Change the on treatment to v2 and the off treatment to `v1.

Defining it in this way means that the default rule and the default treatment will be assigned such that v1 is the default. This is what we would want in our test deployment situation. If you scroll down you’ll see that Set the default rule and Set the default treatment both have v1 as their value.

These two defaults are important to understand. The default rule is the treatment that will be served if none of the targeting rules apply. It’s the default value if the treatment request from the Harness client works properly, but no case is defined in the targeting rules that applies to the user. The default treatment is what will be served if, for example, the Harness client cannot reach the servers or some other fault occurs. It will also be served if the feature flag is killed or the customers are excluded from the feature flag.

Now, you need to define a targeting rule that serves the v2 treatment for the user1 user.

Under Set targeting rules, click Add rule.

You want the rule to read: If user is in list 'user1', serve 'v2'

Click the drop-down and select string and is in list. Add user1 to the text box to the right of the drop-down. Select v2 in the serve drop-down.

Click Save changes at the top of the panel.

Click Confirm.

Your feature flag is configured and active. Now you need to add the Split SDK to the application and add the code that will expose the V2 API using the feature flag. Before you do that, however, you’re going to add some simple authentication to the application to allow for different users.

Add Spring Security 5 Basic Auth to App

Step 11 – In order to have the users to work with, you’re going to add a very simple auth scheme to the API gateway application using Spring Security 5 configured for HTTP basic auth with an in-memory user store.

Open the API gateway project.

Add the Spring Security dependency to the pom.xml file between the <dependencies></dependencies> tags.

Create a SecurityConfiguration class and add the following contents:

src/main/java/com/example/apigateway/SecurityConfiguration.java

This configures Spring Boot to authorize all requests on the resource server and to use HTTP Basic. It also adds our hard-coded, in-memory users: user1 and user2. Hopefully needless to say, this auth scheme is not ready for production and is for the purposes of this tutorial. However, Spring Security is easily configured for use with OAuth 2.0 and OIDC providers, so adapting this to a live scenario would not be that difficult (but that’s a topic for a different tutorial).

Implement the V2 API and the Feature Flag

Step 12 – Still in the API gateway project, add the Split by Harness SDK dependency to the pom.xml file between the <dependencies></dependencies> tags.

Create a new class for to configure the Split by Harness client bean that you’ll inject into the controllers.

src/main/java/com/example/apigateway/SplitConfiguration.java

This code is more-or-less straight out of the Split Java SDK docs.. You’re pulling your Split by Harness API key and building a client bean, setting some default values.

Step 13 – You need to add your Split by Harness API key to your application.properties file. The API keys need to match the environment that you defined your feature flag in. The environment should be Staging-Default if you followed this tutorial.

To find the API keys, click on the DE icon at the top of the left menu. Click Admin settings. Select API keys.

You want the Staging-Default and Server-side keys. Java is a server-side language. Different keys are used for client-side Javascript apps because those keys are essentially public.

Step 14 – Click copy to copy the keys to your clipboard.

In the API gateway properties file, add the following line, replacing {yourSplitApiKey} with your actual API key.

Replace the contents of the main ApiGatewayApplication class with the following:

src/main/java/com/example/apigateway/ApiGatewayApplication.java

Step 15 – There’s a lot going on in this file, so I’ll point some things out. At the top of the file, you now have two Feign clients: one for the V1 API and one for the V2 API. Remember that these Feign clients are what structure the calls to the private weather resource service within the simple microservice (whose actual URI is discovered by the Eureka discovery server based on the name attribute). Notice that I had to set a contextId on these to allow using the same name for both. This name maps to the service name that is used by the Eureka gateway (defined by the spring.application.name property in the weather resource server application.properties file).

If you look at the constructor for the WeatherGatewayService inner class, you’ll see that the Split client is being injected into the constructor along with the two Feign clients.

The splitClient is used in the getTreatmentForPrincipal(Principal) method. This method is called by each of the controller methods. This is where the authenticated username is used to retrieve the treatment for the feature flag. This happens in the splitClient.getTreatment(username, treatment name). You can imagine that the split client is making a request to the Split by Harness servers here and looking up the correct value. However, that could potentially be very slow. The split client does some nice behind-the-scenes caching and polling. For that reason, it doesn’t really need to make a network call to return the treatment; and as such, the getTreatment() method returns almost instantly. Treatment values are still updated within seconds of being changed on the Split by Harness dashboard.

If you look at the endpoints, you’ll see that the first endpoint (/temperature/zip/{zip}) has both a V1 and a V2 definition. The treatment value is used to select the correct Feign client. The two new methods (the second and third endpoints, /temperature/citystatecountry and /weather/zip/{zip}) only have a V2 definition. As such, they throw a Method not found exception if a user with the V1 treatment tries to access them. On all three endpoints, an internal service error is thrown if any other treatment value is returned. This may not be the desired behavior in a production scenario. Maybe instead you want to fallback to the V1 value,but that’s up to you.

Stop the API gateway project with control-c and restart it.

Make sure the Eureka discovery service and the weather resource service are also still running.

Use HTTPie to make some requests.

Both user1 and user2 should be able to make requests on the original endpoint. You’re using HTTPie to pass the the basic auth credentials (-a user1:user2);

You can verify that an unauthenticated request will fail.

According to our treatment, user1 should be able to access the V2 endpoints while user2 should not.

If you look at the console output for these requests, you’ll see that user1 is mapping to the v2 treatment and user2 is mapping to the v1 treatment, exactly as expected.

Now, imagine that the V2 deployment performs flawlessly for a few weeks and you’re ready to enable it for all users. That’s easy to do.

Open your Split dashboard. Go to TARGET and select Splits. Select the v2-deployment split. There are a few ways you could do this. You could add user1 to the list of white-listed users in the targeting rule. However, since you now want V2 to become the standard for all users, you might as well just make it the default rule and default treatment.

Under both Set the default rule and Set the default treatment, select v2.

At the top of the panel, click Save changes. Click Confirm.

Once you click confirm, your changes are live.

Try making a request on the V2 API with user2.

This request just previously failed with a 404 for user2, but because now all users have been authorized to use the V2 API, it works. This was done without changing any code or making any deployments.

You could easily do the opposite, if you wanted. Imagine that the deployment didn’t work (this, of course, would never happen to you, but just for the sake of completeness). In that case, you would want to instead have all users hit the V1 API. To do that, you would delete the targeting rule (under Set targeting rules) and change both the Set the default rule and Set the default treatment back to V1. Save changes and Confirm.

You’re back to getting a 404 for user2 for the V2 API endpoints, but not even user1 will get an error as well.

Both users, however, can still access the V1 endpoint.

Split By Harness Data Logging

Step 16 – The last thing I want to demonstrate is Split’s ability to track events logged through the Split client. The Split client has a track() method that can send arbitrary event data to the Split servers where it is logged. You can read more about this in the Split SDK docs. This is a deep feature that includes the ability to calculate metrics based on tracked events and to trigger alerts. This tutorial won’t go too deep into this ability, except to quickly demonstrate the track() method and introduce you to the API.

Open the ApiGatewayApplication.java file and add the following line to the getWeatherByZip() method as shown below.

This will log an event with Split every time a user uses the V2 API to access the JSON data. Useful, perhaps, if you wanted to know how many users were using the data API endpoint added in the V2 app.

The basic signature of the track() method is as follows.

The method you’re adding above does not log a value. However, numerical values (a Java double) can be logged with each event. You could, for example, track load times or method performance to see if a new release has slowed user access times.

Step 17 – To see this in action, open the Split by Harness dashboard. Click on Data hub from the main menu. Make sure the Staging-Default environment is selected. On the Data type drop-down, select Events. Click the blue Query button. Keep this browser tab open.

This allows you to monitor in real-time the entries created by the splitClient.track() method calls.

Use control-c to stop and restart your API gateway app with ./mvnw spring-boot:run.

Make a few calls to the tracked method.

It may take a few seconds, but you’ll see the track() calls logged.

I won’t demonstrate it here, but if you change the Data type to Impressions, you can track the splitClient.getTreatment() calls as well. Watching all of this come in real-time is fun, but the real power is in the metrics. To learn more about Split by Harness event monitoring and metrics, dig into the Split docs.

Learn More About Spring Boot

In this tutorial, you covered a lot of ground. You saw how to use Spring Cloud to create a scalable, dynamic microservice network that uses a Eureka discovery server to register and lookup services; that implements a private web service that calls outside APIs and re-packages the data for downstream services; and that has a public API gateway that exposes the service to external clients.

You saw how to use Feign to create structured HTTP clients. You also saw how to implement HTTP Basic auth to secure the app using Spring Security 5, and how Split’s implementation of feature flags allows for application behavior to be dynamically changed at runtime. Finally, you had a quick introduction to Split’s event tracking API.

Ready to learn more? We’ve got some additional resources covering all of these topics and more! Check out:

Containerization with Spring Boot and Docker
A Simple Guide to Reactive Java with Spring Webflux
Get Started with Spring Boot and Vue.js

To stay up to date on all things in feature flagging and app building, follow us on Twitter @splitsoftware, and subscribe to our YouTube channel!

Get Split Certified

Split Arcade includes product explainer videos, clickable product tutorials, manipulatable code examples, and interactive challenges.

Deliver Features That Matter, Faster. And Exhale.

Split by Harness is a feature management platform that attributes insightful data to everything you release. Whether your team is looking to test in production, perform gradual rollouts, or experiment with new features–Split by Harness ensures your efforts are safe, visible, and highly impactful. What a Release. Get going with a free account, schedule a demo to learn more, or contact us for further questions and support.

Feature Management & Experimentation