Byte Introduction

Getting started with Spring Boot

Skills:

Java

Objective

Get a quick overview of how to get started with building a simple REST API using Spring Boot.

Background/Recap:

Spring and Spring Boot are vast topics that you can spend weeks together to get a grasp of. It may be much easier to start with some basic understanding of them from wikipedia and then immediately jump into the tasks. When you start using more and more features of Spring, you will start to appreciate why it exists.

Spring

In layman’s terms, it’s an application framework that helps you build Java applications with all bells and whistles really fast.


Spring Boot

Spring Boot is a solution for creating stand-alone, production-grade Spring-based Applications that you can "just run" with very little configuration. In simple terms, it can help you get started in building a REST API server in less than 30 minutes.

References

Primary goals

  1. Build a simple REST API server using Spring Boot

  2. Understand asynchronous flow of a REST API server

  3. Understand some of the basic annotations to create a REST API

  4. Understand Jackson and Java<>JSON conversion

Objective

Get a quick overview of how to get started with building a simple REST API using Spring Boot.

Background/Recap:

Spring and Spring Boot are vast topics that you can spend weeks together to get a grasp of. It may be much easier to start with some basic understanding of them from wikipedia and then immediately jump into the tasks. When you start using more and more features of Spring, you will start to appreciate why it exists.

Spring

In layman’s terms, it’s an application framework that helps you build Java applications with all bells and whistles really fast.


Spring Boot

Spring Boot is a solution for creating stand-alone, production-grade Spring-based Applications that you can "just run" with very little configuration. In simple terms, it can help you get started in building a REST API server in less than 30 minutes.

References

Primary goals

  1. Build a simple REST API server using Spring Boot

  2. Understand asynchronous flow of a REST API server

  3. Understand some of the basic annotations to create a REST API

  4. Understand Jackson and Java<>JSON conversion

Download & run source code

You can download the source code from here using one of the following commands:


git clone https://gitlab.crio.do/crio_bytes/springboot.git

git clone git@gitlab.crio.do:crio_bytes/springboot.git

Tip

We are using a slightly modified version of Spring Boot’s tutorial - https://spring.io/guides/gs/rest-service/. Except for some minor changes like we running it on port 8081, the flow is very similar.

Test the server

Can you start the server now using the following command? It may take upto a minute when you run it the first time as a lot of dependencies get downloaded.


./gradlew bootRun

It will print the Hello World message

if you now visit http://localhost:8081/greeting on your browser or


# type in terminal

curl http://localhost:8081/greeting

You are now running a REST API server which will respond back to your API requests.

What just happened?

You didn’t write a whole lot of code, but in an instant able to run a rest server. That was easy enough, but how did it happen?

  • Open the file - src/main/java/com/example/restservice/GreetingController.java and look for this snippet of code.

// File: src/main/java/com/example/restservice/GreetingController.java

@GetMapping("/greeting")

public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {

 // System.out.println("Learn By Doing");

 Greeting greetingMessage = new Greeting(counter.incrementAndGet(), String.format(template, name));

 return greetingMessage;

}

  • Change @GetMapping("/greeting") to @GetMapping(“/learningisdoing”)

  • Stop ./gradlew bootRun command by pressing ctrl-c and rerun it; this step is called restarting your server.

  • Is your curl command still working? Answer the question given below.

Who calls greeting() function?

Revert the change you did in the previous step and uncomment System.out.println("Learn By Doing") like this:


@GetMapping("/greeting")

public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {

 System.out.println("Learn By Doing");

 return new Greeting(counter.incrementAndGet(), String.format(template, name));

} 

Now restart your server by rerunning ./gradlew bootRun and issue the same curl command again. Do you see "Learn By Doing" printed below ./gradlew bootRun command?


Who is calling greeting() function? Search for it throughout the codebase. Are you able to find the caller? If you are not able to find the function, who calls it?

Tip

You may sometimes see this error when you run ./gradle bootRun command

  • "Web server failed to start. Port 8081 was already in use",

It means that a different instance of your server is running. You may have to kill it before restarting it using lsof -i :8081 and kill commands.

image alt text

Relationship between @GetMapping and function name

  • Now change the function name "public Greeting greeting()" to “public Greeting learningisfun()”.

  • Restart the server and see if you are able to do curl http://localhost:8081/greeting? Answer the question below.

  • Revert the function name back to greeting() once you are done with this experiment.

Servers that continue listening

Did you take a look at main function?


public static void main(String[] args) {

        SpringApplication.run(RestServiceApplication.class, args);

    }

Isn’t your main function supposed to execute this single line SpringApplication.run() and then exit the program? How is that your curl command still able to get replies back if your process terminated after executing this single line?

That’s where Spring’s magic comes into picture - with couple of annotations, Spring is running a a server that keeps listening on a port though your main function completed.

Read a bit about

  • @SpringBootApplication annotation and what it helps with

  • What this line does - SpringApplication.run(RestServiceApplication.class, args)

  • Java annotations

Curious Cats

  • Did we say main() function completed? Doesn’t that mean your process will be killed? Can you prove or disprove whether SpringApplication.run() is a blocking call, ie next line not getting executed unless you stop the server. Tip: Add a System.out.println() right after SpringApplication.run() and see what happens.

Run server in debug mode

  • Set a breakpoint in greeting() function and restart the server in debug mode.

image alt text

  • Run the curl command and observe what you find in the call trace

image alt text

image alt text

As you can see, GreetingController.greeting(String) is the last stack frame, but you are probably not able to find anything else meaningful and you see some functions/variables containing HTTP. That is precisely the point!

Here is what is happening behind the scenes:

  1. You are sending a HTTP request (http://localhost:8081/greeting) to the web server running on port 8081 either through curl or through your browser - both send the exact same request

  2. Your spring server receives this HTTP request, processes it all the way up the stack, and hands it over to some function which is marked with @GetMapping("/greeting") annotation.

  3. Spring uses the @GetMapping annotation to find which function to call - so if you add one more annotation + function, you would have created one more end point and you call it by curl http://localhost:8081/newendpoint


@GetMapping("/newendpoint")

public Greeting functionNameIrrelevant(@RequestParam(value = "name", defaultValue = "World") String name) {

 System.*out*.println("My New Endpoint");

 Greeting greetingMessage = new Greeting(counter.incrementAndGet(), String.format(template, name));

 return greetingMessage;

}

Curious Cats

  • What are the other annotations you see in the code? Can you guess what they do? If you spend time now to understand what they mean, you will save hours of debugging time later when you work on a larger code base.

    • @RestController - Tip: Assume your Java code has 1000s of classes. Which classes should your Spring search for to find @GetMapping?

    • @RequestParam

  • How did we tell the server to listen on 8081 when the tutorial you referred to used 8080? Tip: grep for the "8081" across all files.

  • How do you create an endpoint for a POST request? Look for annotations to do that in Spring.

JSON<->Java

If you had a keen eye, you would have seen greeting() function returning a Java object.


public Greeting greeting()

But the caller of this function, which is your curl command or your browser through indirect hops will not be able to understand Java objects. In fact they received a JSON response.


{

"id":1,

"content":"Hello, World!"

}

How did this happen? Did you ever remember converting the Greeting java object to a JSON string?

Jackson + Spring Magic again

Jackson is a java library that helps you convert a Java object into JSON and back. Whenever required Spring automagically uses Jackson library to convert Java objects into JSON and back. It uses a class in Jackson library called ObjectMapper to do this. Now how do you see for yourself that this is happening? Again breakpoints are our friends.

Set a breakpoint at setName() function in CreditCard.java and run the server in debug mode.

image alt text

Issue the curl command to the json-request endpoint.


curl --header "Content-Type: application/json" \

	  --request POST \

	  --data '{"name":"Santa","creditCardNumber":"0202-2323-2323-1999"}' \

	  http://localhost:8081/json-request

You will be hitting the breakpoint and look at the stack trace. Are you able to see ObjectMapper.readValue()?

image alt text

If you walk down the stack trace further, it will be obvious that Spring called it when trying to process the request.

References

Curious Cats

  • You noticed the setter being called when deserializing the JSON string into a Java object. What do you think will get called when serialization happens? Can you prove that by setting an appropriate breakpoint in Greeting class?

  • How did Jackson know which variable to store creditCardNumber in from the JSON string? Try playing around with the input JSON and variable names in the CreditCard.java class and see if you can make out the relationship.

  • Add @JsonIgnore on top of id field in Greeting.java


@JsonIgnore

private final long id; 

See what output you get for your curl command. Try to read/reason out about the output.

Takeaways

  • Can you now write a simple HTTP end-point using Spring Boot? Btw, no one writes these from scratch including the build.gradle - pick the tutorial code and keep adding more endpoints!

  • Spring Initializr is usually an easy way to get the overall structure of your Spring project including build.gradle dependencies.

  • Are you now comfortable with the following annotations?

    • @RequestParam

    • @GetMapping, @PostMapping

    • @RestController

    • @SpringBootApplication

  • Can you now explain how Spring converts JSON to Java and back?

  • What do serialization and deserialization processes use - getters/setters?