Foundation · Understand

Create Your First Spring Boot Application

Generate a project on Spring Initializr, run it, and serve a first HTTP endpoint—the smallest complete Boot application.

Primary source: Spring Boot 3.5: Developing Your First Application

15 min

Lesson 2 of 5

1d streak

The Architectural Question

Reading about starters and auto-configuration teaches nothing until one generated project boots on your machine and serves a request.

What this guide proves

  • Spring Initializr
  • Running the app
  • First endpoint

Build the smallest complete HTTP application

Generate a Maven project with Java 21 and the Spring Web starter. Keep the generated wrapper in version control so developers and CI use the same build tool. Put the application class in a root package above feature packages so default component scanning can discover them.

TaskApplication.java
package com.example.tasks;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(TaskApplication.class, args);
    }
}
hello/HelloController.java
package com.example.tasks.hello;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
class HelloController {
    @GetMapping("/hello")
    Greeting hello() {
        return new Greeting("Spring Boot is running");
    }
}

record Greeting(String message) {}

Prove each boundary instead of trusting the banner

  1. 01Run ./mvnw spring-boot:run and locate the log line naming the listening port.
  2. 02Call curl -i http://localhost:8080/hello so status, Content-Type, and body are visible.
  3. 03Confirm Jackson converted Greeting into JSON; the controller did not build a JSON string.
  4. 04Run with --server.port=8081 and repeat the request without recompiling.
Terminal / Diagnostic Output
HTTP/1.1 200
Content-Type: application/json

{"message":"Spring Boot is running"}

What the response proves

The application started and the endpoint returned the expected HTTP representation. The startup lifecycle belongs to the SpringApplication lesson; handler dispatch belongs to DispatcherServlet; auto-configuration and condition evidence belong to the auto-configuration lesson.

Follow SpringApplication from main() to ready

SpringApplication.run(TaskApplication.class, args) is the boundary between the JVM entry point and a running Boot application. It prepares the environment, creates the appropriate ApplicationContext, loads configuration, refreshes the context, starts the embedded web server, runs startup callbacks, and publishes the ready event. The exact order matters: a readiness hook must not advertise success before its required initialization has completed.

  1. 01The JVM calls main() and constructs SpringApplication metadata for TaskApplication.
  2. 02Boot prepares profiles, properties, listeners, and the web application type.
  3. 03The ApplicationContext is created and refreshed; definitions are registered and beans are instantiated.
  4. 04The embedded server is started and the application publishes startup callbacks.
  5. 05ApplicationReadyEvent is published only after runners complete successfully.

Active Recall Checkpoint

Why should an unbounded remote call not run in a constructor or @PostConstruct method?

Hint: Which lifecycle stage must complete before the application can advertise readiness?

Show answer & explanation

It can block context refresh and prevent readiness from being reached. Keep bean-local validation small, use bounded startup work in an appropriate runner, and publish readiness only after required initialization succeeds.

Deconstruct @SpringBootApplication without confusing ownership

@SpringBootApplication is a convenience composition, not a new container. It combines @SpringBootConfiguration (the application configuration source), @EnableAutoConfiguration (conditional Boot infrastructure), and @ComponentScan (discovery of application components below the package). Each part has a separate responsibility.

TaskApplicationExpanded.java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class TaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(TaskApplication.class, args);
    }
}

Keep the boundaries separate

@ComponentScan finds your application classes; it does not explain why Jackson or an embedded server is configured. @EnableAutoConfiguration selects conditional infrastructure; it does not widen component scanning. The next lesson focuses on those auto-configuration decisions.

Break package scanning deliberately

Move TaskApplication into com.example.tasks.bootstrap while leaving the controller in the sibling package com.example.tasks.hello. The process still starts, but GET /hello returns 404 because the default scan begins below com.example.tasks.bootstrap. Restore the application class to the common root package and the handler returns.

Active Recall Checkpoint

The process starts and port 8080 accepts connections, but every custom endpoint returns 404. What should you verify before changing controller annotations?

Hint: A live server does not prove that application beans were discovered.

Show answer & explanation

Verify that the application class is in a parent package of controllers and services, or explicitly includes their packages. Then inspect registered mappings before changing endpoint code.

Working Implementation

Code Walkthrough

Generate a web project, start it with ./mvnw spring-boot:run, and answer GET /hello with a plain JSON greeting.

FirstApplicationLab.java
package com.example.firstapp;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@SpringBootApplication
@RestController
public class FirstApplicationLab {

    @GetMapping("/api/v1/ping")
    public Map<String, Object> ping() {
        return Map.of("status", "UP", "framework", "Spring Boot 3.5.16", "java", "21");
    }

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(FirstApplicationLab.class);
        // Programmatic control over startup banner and profiles
        app.setBannerMode(Banner.Mode.OFF);
        app.run(args);
    }
}
SpringApplication.run bootstraps the ApplicationContext and starts embedded Tomcat on port 8080.
Programmatic settings like Banner.Mode.OFF override default bootstrap behaviors.
A single class combining @SpringBootApplication and @RestController provides a functional HTTP service.

Synthesis & Production Verification

Deliberate Failure Condition

  1. Break: Place the main class under com.example.app when code lives in com.example.tasks
  2. Observe: Controllers register no handlers; every request returns 404
  3. Repair: Component scanning starts at the package of @SpringBootApplication and descends from there—package layout is behavior.

Production Acceptance Criteria

  • Generate and run a Boot project without an IDE wizard
  • Return JSON from a @RestController you wrote yourself
  • Explain why the application chose port 8080 and how to change it

Architectural & Technical Review

Core Interview Questions

Q01Why should the application class normally live in a root package?Show answer

@SpringBootApplication includes component scanning from its own package downward. A root placement makes feature packages discoverable without a fragile list of scan overrides.

Q02What does a successful GET /hello prove beyond the process starting?Show answer

It proves that a server accepted the request, MVC mapped it to the controller, the method returned successfully, and a message converter serialized the response representation.

Q03Why commit the Maven or Gradle wrapper?Show answer

The wrapper pins the build-tool distribution used by developers and CI, reducing machine-specific build behavior and removing the need for a separately installed matching version.

Production Pitfalls

Critical Anti-Patterns

  • Pitfall: Treating a startup banner as endpoint proof — A server can start while application beans remain undiscovered. Verify the route, response headers, body, and registered mappings.
  • Pitfall: Generating only through an IDE — The wrapper and command-line path must work so the build is reproducible outside one editor configuration.

Primary Documentation & Specifications