In the next few posts, I will be exploring implementing microservices using rest, messaging, and finally Amazon Web Services (AWS) Lambda. Although the tutorials are largely written in a step-by-step format, we also explore the underlying theory of microservice architecture.
In this first tutorial, I assume Eclipse, Maven, and Postman. If new to Java, then you are strongly recommended to begin by first going through this book, Think Java, along with my accompanying tutorials. There are also many links to excellent YouTube tutorials that accompany the step-by-step tutorials provided. If you are new to Maven and/or Eclipse, then here are two introductory tutorials on Maven and Eclipse.
Let’s begin by building a simple Hello World
rest endpoint using Spring Boot. Spring boot is an easy way to create Spring applications without requiring web server installation, Spring configuration files, and other necessities of standing a Spring application. You can quickly create and run a Spring application. Although useful for tutorials, and it is used in production, if you do not understand more traditional Spring applications, you should also learn the details of more traditional Spring application configuration before going to a job interview. But in this and the next several tutorials, I assume Spring Boot.
- Create a new Maven project named
rest-tutorial
. If you have never created a Maven application in Eclipse, refer to this tutorial. - Replace the content of pom.xml with this content:
="1.0"="UTF-8"
<projectDescription>
<name>rest-tutorial</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
- Create the following two packages:
com.tutorial.spring.application
and com.tutorial.spring.rest
in the rest-tutorial
project. (How to create a package) - Create the class
Hello.java
in the com.tutorial.spring.rest
package. (How to create a class)
package com.tutorial.spring.rest;
public class Hello {
private String greeting;
public String getGreeting() {
return greeting;
}
public void setGreeting(String greeting) {
this.greeting = greeting;
}
}
- Create a class with a
main
method named TutorialApplication
in the com.tutorial.spring.application
package. - Add the
@SpringBootApplication
and @ComponentScan
annotations to the class.
package com.tutorial.spring.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan({ "com.tutorial.spring.rest" })
public class TutorialApplication {
public static void main(String[] args) {
SpringApplication.run(TutorialApplication.class, args);
}
}
An annotation is metadata that provides more information to the compiler and that can be interpreted at runtime (Introduction to annotations). Spring Boot uses the @SpringBootApplication
annotation to signify that a class is the starting point for a Spring Boot application. It also provides default values for the @Configuration
, @EnableAutoConfiguration
, and @ComponentScan
Spring annotations. However, notice we also use the @ComponentScan
annotation because we do not wish to use the provided default value. Instead, we explicitly instruct Spring to scan for Spring classes in the com.tutorial.spring.rest package
.
- Create another class named
HelloController
in the com.tutorial.spring.application.rest
package. - Add the
@RestController
and @RequestMapping
annotations to the class:
package com.tutorial.spring.rest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMethod;
@RestController
@RequestMapping(value="/hello")
public class HelloController {
@RequestMapping(value = "/greeting", method = RequestMethod.GET)
public Hello greeting() {
Hello hello = new Hello();
hello.setGreeting("Hello there.");
return hello;
}
}
The @RequestController
annotation serves two purposes, it defines the class as a Spring controller and as a Rest endpoint (more on @RequestController and @Controller
). The first @RequestMapping
use defines the hello endpoint or http://localhost:8080/hello. The second defines the greeting endpoint and is used with the previous endpoint to form http://localhost/hello/greeting. It defines the http method as GET
(more on GET and POST).
- Build and run the application in Eclipse. You should see log messages from the Spring Boot embedded web server in the Eclipse Console. Note the line that confirms the greeting endpoint was mapped to the method developed.
019-02-24 16:11:52.675 INFO 3491 --- [ main]
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped
"{[/hello/greeting],methods=[GET]}" onto public com.tutorial.spring.rest.Hello
com.tutorial.spring.rest.HelloController.greeting()
- Open a web browser and type http:localhost:8080/hello/greeting in as the address to navigate to the endpoint. You should see the following:
But a web browser is intended for results viable for consumption by an end user. For instance, an HTML page renders in a web browser and is visible to the viewer. A Rest service, in contrast, is intended to be used by another application. Moreover, the endpoint’s purpose is to provide data and not display the data. Or,
REST, or REpresentational State Transfer, is an architectural style for providing standards between computer systems on the web, making it easier for systems to communicate with each other. REST-compliant systems, often called RESTful systems, are characterized by how they are stateless and separate the concerns of client and server. We will go into what these terms mean and why they are beneficial characteristics for services on the Web (code academy).
Moreover, note the displayed results. The results are displayed using JavaScript Object Notation (JSON). This notation is for easy communication between programs rather than human end-user consumption (more on JSON).
Instead of using a browser, let’s use a free tool named Postman. It is designed specifically for Rest developers. Download and install Postman if you do not already have it installed. You can obtain it here. For more information on Postman, refer to the many tutorials provided.
- Start Postman and create a new Collection named
SprintMicroservicesTutorial
.
- Create a new Request named Greeting. Ensure the method is
GET
and has the following URL: http://localhost:8080/greeting (creating a GET request).
- Click
Send
and the following should appear as the response:
{
"greeting": "Hello there."
}
Congratulations! You created your first Rest web service.
API Design
Rest is designed like webpages, except with computers as the consumers. Just as you would almost never navigate to a page conceptually represented by a verb you should never represent a Rest endpoint by such. Use nouns. Endpoints, like webpages, are resources.
Rest endpoints are also designed to be layered into hierarchies, the same as webpages. Suspend disbelief and let’s assume we have a simple endpoint to a dogfood
and catfood
database. Obviously, the example is greatly simplified.
The endpoint is food. The API provides information on food for two species: dogs
and cats
. Each species has thousands of possible breeds. And each food has three possible sizes: small
, medium
, and large
.
For purposes of illustration, we chose to make dog
and cat
two separate endpoints. And because there are thousands of potential breeds, we make them what are called path parameters. Finally, we choose a query parameter to represent the food size.
Path and Query Parameters
Parameters that are part of an endpoint path are termed path parameters. They are distinguished by curly braces. For example:
http://www.president.com/{president}
represents an endpoint where the client replaces the president with the name of the specific president.
A query string
parameter is represented after the endpoint by using a question mark to offset the query string
.
http://ww.president.com/{president}?age=<age>
- Create a new
com.tutorial.spring.rest.petfood
package. Create three new classes named Food
, DogFood
, and CatFood
. Create enumerations for the species
and size
properties.
package com.tutorial.spring.rest.petfood;
public class Food {
public enum Species {DOG, CAT}
public enum Size {SMALL, MEDIUM, LARGE}
private String brand;
private Double price;
private Size size;
private Species species;
private String enteredBreed;
public String getEnteredBreed() {
return enteredBreed;
}
public void setEnteredBreed(String enteredBreed) {
this.enteredBreed = enteredBreed;
}
public Species getSpecies() {
return species;
}
public void setSpecies(Species species) {
this.species = species;
}
public Size getSize() {
return size;
}
public void setSize(Size size) {
this.size = size;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
package com.tutorial.spring.rest.petfood;
public class DogFood extends Food {
}
package com.tutorial.spring.rest.petfood;
public class CatFood extends Food {
}
- Create a class named
FoodController
. Provide it with a @RestController
annotation and a top-level @RequestMapping
for the /food
endpoint. - Create two endpoint methods, one for the
/dog
endpoint and one for the /cat
endpoint. - Add the {breed} path parameter to both methods:
package com.tutorial.spring.rest.petfood;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/food")
public class FoodController {
@RequestMapping(value = "/dog/{breed}", method = RequestMethod.GET)
public List<DogFood> dogFoodrecommendation(@PathVariable String breed,
@RequestParam("size") Food.Size size){
}
@RequestMapping(value = "/cat/{breed}", method = RequestMethod.GET)
public List<CatFood> catFoodrecommendation(@PathVariable String breed,
@RequestParam("size") Food.Size size){
}
}
The {breed}
combined with the @PathVariable
annotation is how you define a path parameter. The @RequestParam
annotation is how you define a parameter bound to a query parameter.
- Create a new class named
FoodService
and add simple methods to create a list containing cat food and a list containing dog food. - Annotate the class with the
@Service
annotation:
package com.tutorial.spring.rest.petfood;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.tutorial.spring.rest.petfood.Food.Size;
import com.tutorial.spring.rest.petfood.Food.Species;
@Service
public class FoodService {
public List<CatFood> createCatFood(String breed, Size size)
{
List<CatFood> food = new ArrayList<CatFood>();
CatFood a = new CatFood();
a.setBrand("Purina");
a.setPrice(13.12);
a.setSize(size);
a.setSpecies(Species.CAT);
a.setEnteredBreed(breed);
food.add(a);
CatFood b = new CatFood();
b.setBrand("Science Diet");
b.setPrice(10.00);
b.setSize(size);
b.setSpecies(Species.CAT);
b.setEnteredBreed(breed);
food.add(b);
return food;
}
public List<DogFood> createDogFood(String breed, Size size)
{
List<DogFood> food = new ArrayList<DogFood>();
DogFood a = new DogFood();
a.setBrand("Purina");
a.setPrice(33.22);
a.setSize(size);
a.setSpecies(Species.DOG);
a.setEnteredBreed(breed);
food.add(a);
DogFood b = new DogFood();
b.setBrand("Science Diet");
b.setPrice(12.22);
b.setSize(size);
b.setSpecies(Species.DOG);
b.setEnteredBreed(breed);
food.add(b);
return food;
}
}
The annotation causes the class to be scanned by Spring when performing classpath scanning. Spring registers the class as a service. An instance of this class is then available to other classes without explicit instantiation using a constructor. You then auto-wire it to other classes using the @Autowired
annotation.
- Modify the
FoodController
class to auto-wire the FoodService
class. - Modify the two methods to call the service classes
createDogFood
and createCatFood
methods respectively.
package com.tutorial.spring.rest.petfood;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/food")
public class FoodController {
@Autowired
private FoodService foodService;
@RequestMapping(value = "/dog/{breed}", method = RequestMethod.GET)
public List<DogFood> dogFoodrecommendation(@PathVariable String breed,
@RequestParam("size") Food.Size size){
return foodService.createDogFood(breed, size);
}
@RequestMapping(value = "/cat/{breed}", method = RequestMethod.GET)
public List<CatFood> catFoodrecommendation(@PathVariable String breed,
@RequestParam("size") Food.Size size){
return foodService.createCatFood(breed, size);
}
}
The @Autowired
annotation marks an object as automatically injected using Spring’s dependency injection. An instance of the FoodService
class is annotated with @Service
and so is loaded by Spring and subsequently injected into the FoodController
instance. The FoodController
instance can then use the Foodservice
instance as if it had explicitly created it using a constructor.
- Create two new
GET
requests in Postman. One for cat food and one for dog food. - For the cat endpoint, enter
tiger
as breed and LARGE
as size.
- For the dog endpoint, enter
chihuahua
as breed and SMALL
as size.
What is returned is an array of cat foods and a list of dog foods (JSON arrays).
In this tutorial, we used Spring Boot to create several Rest endpoints. You learned a little of the reasoning behind the idea of a Restful API.