I was recently listening to the Arrested DevOps podcast, in the episode on Making DevOps Beginner Friendly guest Laura Santamaria talked about the importance of creating learning paths. A learning path, as the name suggests, is a series of articles or guides that walk someone through how to use a technology or practice. Learning paths differ from normal blog articles, like I have often done, which cover how to accomplish a very specific goal in isolation.
In the decade I have been working with the Spring Framework in general and the 5 years I have specifically worked with Spring Boot I have learned a lot, what to do, what not to do, and in some cases the why behind some of those answers. With many people working from home in response to the COVID-19 outbreak, seems an opportune time to go back to the basics.
In this series we will do a slow burn through Spring Boot, each article will be structured around the steps to do a complete a common task, but will take the time to explain what exactly the code is doing, what is happening in the background, as well as some of the why/best practices behind the tasks. The goal isn’t necessarily to break new ground in what Spring Boot can do, but to try to get a more well-rounded understanding of Spring Boot.
In this first article of the we will initializing a new Spring Boot project and create a couple of simple HTTP GET
endpoints. So with that…
Initializing a Spring Boot Project
When starting a new Spring Boot project, one of the best places to go is start.spring.io. start.spring.io provides an interface for defining a project’s metadata as well as the ability to easily bring in many commonly used dependencies that should all be compatible to work with one another. Below demonstrates how to quickly initialize a project:
Note: If you are following along with this article you should bring in the spring-web
and spring-boot-devtools
dependencies.
Building Web APIs with Spring Boot
For many Java developers, a big part of their day is spent building and maintaining applications that service a Web API. Spring Boot makes building and maintaining really easy, which is a big reason why it has become so popular in the Java world.
After importing a new Spring Boot application into your preferred IDE, we can have an accessible endpoint with just these few lines of code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RestController | |
@RequestMapping("/api/v1/hello") | |
public class HelloSpringController { | |
@GetMapping | |
public String helloWorld() { | |
return "Hello World"; | |
} | |
} |
Once added, starting the Spring Boot application should result in “Hello World!” being printed when you go to: http://localhost:8080/api/v1/hello.
Let’s look at the key elements from the above:
@RestController
: This annotation marks to Spring that this class is a web controller, a class that serves as the interface to the Web for interacting with the internal application.
@RequestMapping("/api/v1/hello")
: This annotation allows a developer to define the base path for the entire controller. All endpoints defined in this controller will be pre-fixed with /api/v1/hello
.
@GetMapping
: This annotation defines that the method helloWorld
can be accessed as a HTTP GET.
With “Hello World” working, the second task when working with a new language or framework is to take in some user input to create a message. With a GET
endpoint there are three ways of accepting input from a client; via the URL path, as query parameters, and as a request headers. Let’s look at how to reference values from each below.
Retrieve Values from the URL Path
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@GetMapping("/{message}") | |
public String helloMessage(@PathVariable String message) { | |
return String.format("Hello, %s!", message); | |
} |
To retrieve values from the URL path, in the @GetMapping
you will need to define a variable in enclosing braces like above with {message}
. In the arguments of the method an argument must be annotated with @PathVariable
, if the name of the argument is the same as the variable in the definition of @GetMapping
then Spring will automatically map it. @PathVariable
has three fields:
name
: Allows for manually mapping a url path variable a method argument.
required
: Boolean for if the path value is required. Defaults to true.
value
: alias for name.
Retrieve Values from the URL Query
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@GetMapping("/name") | |
public String helloQueryMessage(@RequestParam String firstName, @RequestParam String lastName) { | |
return String.format("Hello %s %s!", firstName, lastName); | |
} |
Values can easily be retrieved from the query portion of an URL, the section of the URL after the “?” e.g.: ?firstName=Billy&lastName=Korando
. Spring by default will attempt map query variables to the names of arguments in the method. So in the example URL query firstName
and lastName
will automatically map to the arguments firstName
and lastName
. @RequestHeader
has four fields:
name
: Allows for manually mapping a query value to a method argument.
required
: Boolean for if the path value is required. Defaults to true. A HTTP 400
is thrown if a required value is not provided.
defaultValue
: A default value for when the parameter is not provided. Will set required to false.
value
: alias for name.
Retrieve Values from the Request Header
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@GetMapping("/header") | |
public String welcomeUser(@RequestHeader String user) { | |
return String.format("Welcome %s!", user); | |
} |
Retrieving values from a request header works very similarly to retrieving them from the URL query. Like with @RequestParam
, @RequestHeader
will automatically map the method argument name to the name of a header value. @RequestHeader
has four fields:
name
: Allows for manually mapping a header value to a method argument.
required
: Boolean for if the path value is required. Defaults to true. A HTTP 400
is thrown if a required value is not provided.
defaultValue
: A default value for when the parameter is not provided. Will set required to false.
value
: alias for name.
String.format() or String Concatenate
Commonly when building a String
in Java many Java developers build a String
using concatenation like this:
"A message with a variable: " + var1 + " and another variable: " + var2 + " and more...";
Constructing a String
this way can become difficult to read, and also be a formatting nightmare as the code is constantly changed because of slightly different formatting rules. When building a String
it can be useful to consider using String.format()
instead as demonstrated above. Readability can be a bit easier and there a number of pre-defined ways for printing things like dates available. For more information on how to use String.format()
check out the official Javadoc: 8, 11, 14
Convention over Configuration
I am a longtime Spring user, my first experience with Spring was in 2010, using then Spring 2.5. While Spring was a significant improvement over frameworks I had used prior, initializing a new Spring project was still a difficult and and time consuming process process. Getting a static endpoint running as we have done in this article could take hours, even days, if starting truly from scratch.
We were able to accomplish in minutes with Spring Boot, what took hors before because Spring Boot uses a pattern called convention over configuration. In short Spring Boot has a number of default opinions, such as using an embedded Apache Tomcat server running on port 8080
. Many of of these opinions however can easily be change. If we needed to run our Spring Boot application on a different port, we can just set server.port
. Using a different application server can be as easy as making a couple small changes to our build file.
Convention over configuration allows developers to focus on key business concerns, because in many cases using embedded Apache Tomcat and running on port 8080
is enough, especially in an increasingly containerized world. Understanding Spring Boot’s default opinions and how to change them will be a key element through out this series because there are definitely right and wrong ways of changing them.
Restart Revolution
As an application is being built there is often a need to rapidly iterate. This means rebuilding and restarting an application frequently. While the steps to rebuild and restart an application aren’t difficult, performing them can disrupt your “flow”. To address this the Spring team developed Spring Boot devtools. Spring Boot devtools provide two key features; automated start and, with browser extensions, live reload. Here is Spring Boot devtools in action:
To use Spring Boot devtools in your project, you will need to add it as a dependency in your build file like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-devtools</artifactId> | |
<optional>true</optional> | |
</dependency> |
Be sure to check out the user guides for more information on how to use Spring Boot Devtools including how to include exclude additional files, use it within a production system, using it on a remote system, and more.
Proper REST and API Best Practices
Like the code in a project itself, the usability and longterm maintainability of an API depends significantly on how well it is designed. Let’s review a few ways to improve the design of an API.
Version Your API
Probably the first, and also on of the easiest ways, to improve the design an API is to include in the URL the API version. In the examples about this was done with v1
. Versioning an API allows it to more easily evolve over time as business and client need changes. When breaking changes are introduced, they can be included a new version of the API e.g. v2
and this much easier for clients to migrate to than forcing a hard and complicated switch if the same API endpoints are used.
Follow REST When Practical
Representational State Transfer, or REST, has become a popular architecture to follow when designing Web based APIs. REST was built upon the HTTP protocol, and while there are legitimate critiques that it might not always work well in every business case, there are a few good elements to follow such as; using the appropriate HTTP verb for the behavior of a endpoint e.g., for retrieving data a GET
should be used, creating new resources should be a POST
, DELETE
for when an resource should be deleted.
Additionally proper usage of HTTP codes can be helpful as well; a HTTP 200
should be returned only when a request is successful. 400
should be returned, along with an appropriate message, when the client sends invalid or bad data. A 404
is also appropriate to return when a client requests a non-existent resource.
Fully following all of REST might not be possible or practical in all use cases, but following some of the key elements above can greatly improve the usability and maintainability of a API.
Conclusion
Spring Boot has been a revelation for the Spring developer community. Spring Boot has allowed developers to quickly build new applications while focusing on designing business valuable features for their organizations. As touched on in this article, there is also a lot of subtly to using Spring Boot. Spring Boot is easy to get started with, but can take a lot to “master”, as even after five years I am still learning new things all the time. In this series we will continue to explore how to use Spring Boot to its full potential.
The code examples used in this article can be found in my GitHub.
2 thoughts on “Spring Bootcamp – GETting Started”