Java Tutorial

Lesson 6: Spring Boot Hello World

A first Spring Boot web app with Maven, @SpringBootApplication, @RestController, query parameters, JSON POST requests, configuration properties, service validation, error handling, and tests.

Run

scripts/run.sh lesson_6 demo
scripts/run.sh lesson_6 exercise
scripts/apply_answers.sh lesson_6
scripts/run.sh lesson_6 verify
scripts/reset_exercises.sh lesson_6

Function Pages

45 pages generated for Java functions in this lesson.

View related functions

Lesson README

Lesson 6: Spring Boot Hello World

This lesson starts a Spring Boot tutorial from the smallest useful web app: a GET /hello endpoint that returns JSON. It then extends the hello-world example with query parameters, a JSON POST, configuration properties, service-layer validation, error handling, and tests.

The lesson uses Spring Boot 4.1.0, Java 21, and Maven. Spring Boot's official project page describes Boot as a way to create stand-alone Spring applications with embedded servers and starter dependencies. The official system requirements for Spring Boot 4.1.0 require Java 17 or newer and Maven 3.6.3 or newer.

References:

120-Minute Plan

MinutesActivity
0-10Explain what Spring Boot adds to plain Java: application context, auto-configuration, embedded server, starters, and runnable app packaging.
10-25Run the exercise stub and the demo. Compare command-line object calls with HTTP JSON calls.
25-45Read SpringHelloApplication, HelloController, and GreetingConfiguration to trace how a request reaches a service.
45-65Study GreetingService as the learner-editable contract. Identify name defaults, whitespace handling, punctuation rules, and max-length validation.
65-95Implement GreetingService.hello, normalizeName, and normalizePunctuation.
95-110Apply answers and run the Maven verifier. Read each failing test as a Spring Boot behavior requirement.
110-120Start the app on local port 8080 and call it with curl or a browser.

Commands

Run the safe stub exercise:

scripts/run.sh lesson_6 exercise

Run a real embedded server demo that starts on a random port, calls itself, and exits:

scripts/run.sh lesson_6 demo

Apply answers and verify:

scripts/apply_answers.sh lesson_6
scripts/run.sh lesson_6 verify
scripts/reset_exercises.sh lesson_6

Start the real hello app on local port 8080:

scripts/serve_spring_hello_8080.sh

The default bind address is 0.0.0.0. To bind only to loopback:

SERVER_ADDRESS=127.0.0.1 SERVER_PORT=8080 scripts/serve_spring_hello_8080.sh

Try these requests:

curl http://127.0.0.1:8080/
curl 'http://127.0.0.1:8080/hello?name=Ada'
curl 'http://127.0.0.1:8080/hello?name=Linus&punctuation=%3F'
curl -X POST http://127.0.0.1:8080/hello \
  -H 'Content-Type: application/json' \
  -d '{"name":"Grace Hopper","punctuation":"."}'
curl 'http://127.0.0.1:8080/hello?name=Ada&punctuation=%40'

Exercise

Edit:

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingService.java

Functions to implement:

The lesson compiles before implementation. The stub always returns the classic Hello, World! shape. The answer verifier checks defaults, trimming, whitespace collapse, punctuation validation, max-name length, GET /hello, POST /hello, and HTTP 400 error handling.

Learner Workbook

Learner Workbook: Lesson 6 Spring Boot Hello World

This workbook is designed to take about two hours. The goal is to understand the first Spring Boot path from main to HTTP request to JSON response, while still practicing normal Java service design.

What You Should Understand By The End

0-10 Minutes: Boot Mental Model

Plain Java starts with:

public static void main(String[] args)

Spring Boot still starts there, but SpringApplication.run does extra work:

Open pom.xml. Find:

10-25 Minutes: Run The Stub And Demo

Run:

scripts/reset_exercises.sh lesson_6
scripts/run.sh lesson_6 exercise
scripts/run.sh lesson_6 demo

The exercise runner calls Java objects directly. The demo starts a real embedded server on a random port, sends HTTP requests to it, prints the JSON, and shuts down.

Write down the difference between:

25-45 Minutes: Trace A Request

Open these files in order:

Trace this request:

GET /hello?name=Ada&punctuation=.

Answer these questions:

45-65 Minutes: Define The Service Contract

GreetingService is the exercise file. Its rules are intentionally small:

Examples:

InputExpected message
name="", punctuation=""Hello, World!
name="Ada", punctuation="?"Hello, Ada?
name=" Grace Hopper ", punctuation="."Hello, Grace Hopper.
name="Ada", punctuation="@"HTTP 400 through the controller

65-95 Minutes: Implement In Rounds

Edit GreetingService.java.

Round 1: normalizeName

Round 2: normalizePunctuation

Round 3: hello

Run after each round:

scripts/run.sh lesson_6 exercise

95-110 Minutes: Verify

Run:

scripts/apply_answers.sh lesson_6
scripts/run.sh lesson_6 verify
scripts/reset_exercises.sh lesson_6

The verifier is a Maven test suite. Read test names as requirements:

110-120 Minutes: Run On Port 8080

Start the app:

scripts/serve_spring_hello_8080.sh

In another terminal:

curl http://127.0.0.1:8080/
curl 'http://127.0.0.1:8080/hello?name=Ada'
curl 'http://127.0.0.1:8080/hello?name=Linus&punctuation=%3F'
curl -X POST http://127.0.0.1:8080/hello \
  -H 'Content-Type: application/json' \
  -d '{"name":"Grace Hopper","punctuation":"."}'
curl 'http://127.0.0.1:8080/hello?name=Ada&punctuation=%40'

Stop the app with Ctrl+C.

Edge-Case Drill

Before leaving the lesson, predict each result:

Then run the request and compare.

Instructor Notes

Instructor Notes: Lesson 6 Spring Boot Hello World

Teaching Goal

This is the first framework lesson. Keep the frame tight: Spring Boot does not replace Java fundamentals. It wires Java objects together, starts infrastructure, and maps external requests onto ordinary methods.

Key Teaching Points

Suggested Live Flow

Common Confusions

Extension Prompts

Rescue Prompts

Functions In This Lesson

GreetingService.GreetingService

lesson_6/answers/java/tutorial/lesson6/hello/GreetingService.java:10 | constructor | answer

public GreetingService(GreetingProperties properties) {

GreetingService.hello

lesson_6/answers/java/tutorial/lesson6/hello/GreetingService.java:15 | method | answer

public GreetingResponse hello(String rawName, String rawPunctuation) {

GreetingService.normalizeName

lesson_6/answers/java/tutorial/lesson6/hello/GreetingService.java:22 | method | answer

public String normalizeName(String rawName) {

GreetingService.normalizePunctuation

lesson_6/answers/java/tutorial/lesson6/hello/GreetingService.java:34 | method | answer

public String normalizePunctuation(String rawPunctuation) {

ApiExceptionHandler.badRequest

lesson_6/src/exercise/java/tutorial/lesson6/hello/ApiExceptionHandler.java:10 | method | exercise

public ResponseEntity<ErrorResponse> badRequest(IllegalArgumentException exception) {

BootHelloDemo.BootHelloDemo

lesson_6/src/exercise/java/tutorial/lesson6/hello/BootHelloDemo.java:13 | constructor | exercise

private BootHelloDemo() {

BootHelloDemo.main

lesson_6/src/exercise/java/tutorial/lesson6/hello/BootHelloDemo.java:16 | method | exercise

public static void main(String[] args) throws IOException, InterruptedException {

BootHelloDemo.get

lesson_6/src/exercise/java/tutorial/lesson6/hello/BootHelloDemo.java:36 | method | exercise

private static String get(HttpClient client, int port, String pathAndQuery) throws IOException, InterruptedException {

BootHelloDemo.post

lesson_6/src/exercise/java/tutorial/lesson6/hello/BootHelloDemo.java:43 | method | exercise

private static String post(HttpClient client, int port, String path, String json) throws IOException, InterruptedException {

BootHelloDemo.uri

lesson_6/src/exercise/java/tutorial/lesson6/hello/BootHelloDemo.java:53 | method | exercise

private static URI uri(int port, String pathAndQuery) {

ErrorResponse.ErrorResponse

lesson_6/src/exercise/java/tutorial/lesson6/hello/ErrorResponse.java:4 | compact constructor | exercise

public ErrorResponse {

ExerciseRunner.ExerciseRunner

lesson_6/src/exercise/java/tutorial/lesson6/hello/ExerciseRunner.java:4 | constructor | exercise

private ExerciseRunner() {

ExerciseRunner.main

lesson_6/src/exercise/java/tutorial/lesson6/hello/ExerciseRunner.java:7 | method | exercise

public static void main(String[] args) {

GreetingConfiguration.greetingService

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingConfiguration.java:9 | method | exercise

public GreetingService greetingService(GreetingProperties properties) {

GreetingProperties.defaultText

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingProperties.java:16 | method | exercise

private static String defaultText(String candidate, String fallback) {

GreetingRequest.GreetingRequest

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingRequest.java:4 | compact constructor | exercise

public GreetingRequest {

GreetingResponse.GreetingResponse

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingResponse.java:4 | compact constructor | exercise

public GreetingResponse {

GreetingService.GreetingService

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingService.java:6 | constructor | exercise

public GreetingService(GreetingProperties properties) {

GreetingService.hello

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingService.java:11 | method | exercise

public GreetingResponse hello(String rawName, String rawPunctuation) {

GreetingService.normalizeName

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingService.java:16 | method | exercise

public String normalizeName(String rawName) {

GreetingService.normalizePunctuation

lesson_6/src/exercise/java/tutorial/lesson6/hello/GreetingService.java:21 | method | exercise

public String normalizePunctuation(String rawPunctuation) {

HelloController.HelloController

lesson_6/src/exercise/java/tutorial/lesson6/hello/HelloController.java:15 | constructor | exercise

public HelloController(GreetingService greetings) {

HelloController.index

lesson_6/src/exercise/java/tutorial/lesson6/hello/HelloController.java:21 | method | exercise

public Map<String, String> index() {

HelloController.helloJson

lesson_6/src/exercise/java/tutorial/lesson6/hello/HelloController.java:38 | method | exercise

public GreetingResponse helloJson(@RequestBody GreetingRequest request) {

HelloController.health

lesson_6/src/exercise/java/tutorial/lesson6/hello/HelloController.java:44 | method | exercise

public Map<String, String> health() {

SpringHelloApplication.main

lesson_6/src/exercise/java/tutorial/lesson6/hello/SpringHelloApplication.java:10 | method | exercise

public static void main(String[] args) {

SpringHelloApplicationTest.get

lesson_6/src/test/java/tutorial/lesson6/hello/SpringHelloApplicationTest.java:85 | method | test

private HttpResponse<String> get(String pathAndQuery) throws IOException, InterruptedException {

SpringHelloApplicationTest.post

lesson_6/src/test/java/tutorial/lesson6/hello/SpringHelloApplicationTest.java:91 | method | test

private HttpResponse<String> post(String path, String json) throws IOException, InterruptedException {

SpringHelloApplicationTest.uri

lesson_6/src/test/java/tutorial/lesson6/hello/SpringHelloApplicationTest.java:100 | method | test

private URI uri(String pathAndQuery) {

GreetingService.GreetingService

lesson_6/stubs/java/tutorial/lesson6/hello/GreetingService.java:6 | constructor | stub

public GreetingService(GreetingProperties properties) {

GreetingService.hello

lesson_6/stubs/java/tutorial/lesson6/hello/GreetingService.java:11 | method | stub

public GreetingResponse hello(String rawName, String rawPunctuation) {

GreetingService.normalizeName

lesson_6/stubs/java/tutorial/lesson6/hello/GreetingService.java:16 | method | stub

public String normalizeName(String rawName) {

GreetingService.normalizePunctuation

lesson_6/stubs/java/tutorial/lesson6/hello/GreetingService.java:21 | method | stub

public String normalizePunctuation(String rawPunctuation) {