Your First Spring Boot Application with Spring Initializr

Introduction

This tutorial creates your first Spring Boot application, a small web application that answers HTTP requests, starting from Spring Initializr, the official project generator. We will start with what Spring Initializr is and the options it asks for, and generate a project from the website or from the command line. We will then walk through the generated project - its folders, its pom.xml and its main class - and add a REST controller. Next we will run the application, call it with real HTTP requests, and read its startup log line by line. Lastly, we will run the test that Initializr generates and look at where to go from there.

(This tutorial starts the Spring Boot module of the Spring series. If you are new to Spring, What is Spring? Spring Framework vs Spring Boot ExplainedClick to read tutorial: What is Spring? Spring Framework vs Spring Boot Explained explains how Spring Boot relates to the Spring Framework.)

What is Spring Initializr?

Spring Initializr, at start.spring.io, generates the skeleton of a Spring Boot project: a build file with the right dependencies and plugin, a main class, a configuration file, a first test, and a build tool wrapper. You fill in a short form, click Generate, and download a ZIP file that is ready to open in an IDE and run.

The same generator is built into IntelliJ IDEA (New Project > Spring Boot), VS Code (the Spring Boot Extension Pack), and Eclipse with the Spring Tools, and it has an HTTP API that tools and scripts can call. Whichever you use, the result is the same project.

The options on start.spring.io

The form asks for a few choices. For this tutorial's application, they are -
  • Project: the build tool - Maven, Gradle - Groovy or Gradle - Kotlin. This series uses Maven.
  • Language: Java, Kotlin or Groovy. We use Java.
  • Spring Boot: the Boot version. Initializr pre-selects the latest stable release, which at the time of writing is 4.1.2; versions marked SNAPSHOT or RC are previews and not meant for real projects.
  • Project Metadata: Group com.javabrahman, Artifact first-app, a Description, and the Package name com.javabrahman.firstapp, which Initializr derives from the group and artifact.
  • Packaging: Jar, the usual choice for Spring Boot, which runs with its own embedded web server. War is for deploying to an external server.
  • Java: the Java version - 17, 21, 25 or 27 at the time of writing. Spring Boot 4 needs at least Java 17; this series uses Java 25, the current long-term-support release.
  • Dependencies: the features the application needs. We add only Spring Web, for building web applications and REST APIs with Spring MVC.
Clicking Generate downloads first-app.zip. The same project can be generated from a terminal through Initializr's HTTP API -
curl https://start.spring.io/starter.zip -d type=maven-project -d bootVersion=4.1.2 -d javaVersion=25 -d groupId=com.javabrahman -d artifactId=first-app -d packageName=com.javabrahman.firstapp -d dependencies=web -o first-app.zip

The generated project

Unzipped, the project looks like this -
first-app
|-- .gitattributes
|-- .gitignore
|-- HELP.md                        links to the documentation of the chosen dependencies
|-- mvnw, mvnw.cmd                 the Maven wrapper scripts, for Unix and Windows
|-- .mvn/wrapper/                  wrapper settings: which Maven version to download
|-- pom.xml                        the Maven build file
`-- src
    |-- main
    |   |-- java/com/javabrahman/firstapp
    |   |   `-- FirstAppApplication.java     the main class
    |   `-- resources
    |       |-- application.properties       configuration
    |       |-- static/                      static web files (HTML, CSS, JavaScript)
    |       `-- templates/                   server-side templates, if a template engine is added
    `-- test/java/com/javabrahman/firstapp
        `-- FirstAppApplicationTests.java    a first test
The Maven wrapper (mvnw) downloads the right version of Maven the first time it runs, so the project builds on any machine with a JDK, even one without Maven installed. In the commands below, ./mvnw (or mvnw.cmd on Windows) can always replace mvn.

The pom.xml

Here is the pom.xml exactly as Initializr generated it for our choices -
pom.xml generated by Spring Initializr
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>4.1.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javabrahman</groupId>
	<artifactId>first-app</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>first-app</name>
	<description>My first Spring Boot application</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>25</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webmvc</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webmvc-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Explanation of the pom.xml

  • The parent, spring-boot-starter-parent version 4.1.2, gives the project Spring Boot's defaults: the versions of hundreds of libraries that are known to work together, compiler settings, resource filtering, and plugin configuration. That is why the dependencies below have no <version>.
  • java.version 25 sets the Java version the code is compiled for.
  • spring-boot-starter-webmvc is the starter for the Spring Web dependency. It brings Spring MVC, an embedded Tomcat server and JSON support in one dependency. (In Spring Boot 3 and earlier it was called spring-boot-starter-web; Spring Boot 4 renamed it.) Starters are covered in detail in Spring Boot Starters ExplainedClick to read tutorial: Spring Boot Starters Explained with Examples.
  • spring-boot-starter-webmvc-test, with test scope, brings JUnit 5, AssertJ, Mockito and Spring's test support - used only for compiling and running tests, never packaged with the application.
  • The spring-boot-maven-plugin adds the spring-boot:run goal and packages the application as an executable jar, covered in Packaging Spring Boot AppsClick to read tutorial: Packaging Spring Boot Apps: Executable and Layered JARs.
  • The empty url, licenses, developers and scm elements are placeholders for project information; they can be filled in or deleted.

The main class

The only Java code Initializr generates is the main class -
FirstAppApplication.java generated by Spring Initializr
package com.javabrahman.firstapp;

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

@SpringBootApplication
public class FirstAppApplication {

	public static void main(String[] args) {
		SpringApplication.run(FirstAppApplication.class, args);
	}

}
@SpringBootApplication marks this as the application's configuration class and switches on three things: component scanning of this class's package and its sub-packages, auto-configuration, and the ability to declare @Bean methods here. SpringApplication.run() starts the application: it creates the application context, applies the auto-configuration, and - because Spring MVC is on the classpath - starts an embedded Tomcat web server. How auto-configuration decides what to set up is explained in How Spring Boot Auto-Configuration WorksClick to read tutorial: How Spring Boot Auto-Configuration Works.

The generated application.properties holds a single line, spring.application.name=first-app. The name appears in every log line, as we will see.

Adding a REST controller

The generated application starts, but it has nothing to say. We add one class next to the main class: a REST controller with two endpoints, one returning text and one returning JSON -
HelloController.java - two REST endpoints
package com.javabrahman.firstapp;

import java.time.LocalDate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

	@GetMapping("/hello")
	public String hello(@RequestParam(defaultValue = "World") String name) {
		return "Hello, " + name + "!";
	}

	@GetMapping("/info")
	public AppInfo info() {
		return new AppInfo("first-app", "Spring Boot", LocalDate.of(2026, 11, 6));
	}

	public record AppInfo(String application, String builtWith, LocalDate date) {
	}

}

Explanation of the code

  • @RestController makes the class a bean (component scanning finds it, since it is in the main class's package) and tells Spring MVC that its methods return response bodies rather than view names.
  • @GetMapping("/hello") maps HTTP GET requests for /hello to the hello() method.
  • @RequestParam(defaultValue = "World") binds the query parameter name, as in /hello?name=Nancy, falling back to World when it is absent.
  • info() returns a recordClick to read: record. Spring Boot converts the returned object to JSON automatically, using the Jackson library that the web starter includes.
REST controllers are covered in depth in module 3 of this series.

Running the application

From the project folder, start the application with the Spring Boot Maven plugin -
./mvnw spring-boot:run
Maven compiles the code and starts the application, which keeps running until you stop it with Ctrl+C. While it runs, the endpoints can be called from a browser or from another terminal with curl. These are the real responses from the running application -  OUTPUT of the HTTP requests
> curl http://localhost:8080/hello
Hello, World!

> curl http://localhost:8080/hello?name=Nancy
Hello, Nancy!

> curl http://localhost:8080/info
{"application":"first-app","builtWith":"Spring Boot","date":"2026-11-06"}

> curl http://localhost:8080/nothing-here
HTTP 404 {"timestamp":"2026-11-06T08:05:59.138Z","status":404,"error":"Not Found","path":"/nothing-here"}
The first two requests reached hello(), with and without the name parameter. /info returned the record as JSON, with the LocalDate written as an ISO date. /nothing-here has no mapping, so Spring Boot answered with status 404 and a JSON error body - its built-in error handling, which needs no code.

Reading the startup log

Meanwhile, the application wrote this log to its console -  OUTPUT - the application's console log
2026-11-06T13:35:56.533+05:30  INFO 28648 --- [first-app] [           main] o.s.boot.tomcat.TomcatWebServer          : Tomcat initialized with port 8080 (http)
2026-11-06T13:35:56.611+05:30  INFO 28648 --- [first-app] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2026-11-06T13:35:56.612+05:30  INFO 28648 --- [first-app] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/11.0.27]
2026-11-06T13:35:56.816+05:30  INFO 28648 --- [first-app] [           main] b.w.c.s.WebApplicationContextInitializer : Root WebApplicationContext: initialization completed in 2948 ms
2026-11-06T13:35:58.180+05:30  INFO 28648 --- [first-app] [           main] o.s.boot.tomcat.TomcatWebServer          : Tomcat started on port 8080 (http) with context path '/'
2026-11-06T13:35:58.681+05:30  INFO 28648 --- [first-app] [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2026-11-06T13:35:58.682+05:30  INFO 28648 --- [first-app] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2026-11-06T13:35:58.687+05:30  INFO 28648 --- [first-app] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 4 ms

Explanation of the log

  • Each line shows the date and time, the level (INFO), the process id, the application name from spring.application.name, the thread, a shortened logger name, and the message. The format is explained in the tutorial on logging in Spring BootClick to read tutorial: Logging in Spring Boot: SLF4J, Logback and Log Levels.
  • Tomcat initialized with port 8080: auto-configuration found Tomcat on the classpath and created an embedded server on the default port, 8080.
  • Starting Servlet engine: [Apache Tomcat/11.0.27] shows the Tomcat version that Spring Boot 4.1.2 manages.
  • Root WebApplicationContext: initialization completed: the application context, with all beans including our controller, is ready.
  • Tomcat started on port 8080 (http) with context path '/': the server is accepting requests.
  • The DispatcherServlet lines appeared only when the first request arrived, on a Tomcat request thread (nio-8080-exec-...): Spring MVC's front controller, which routes every request to a controller method, is initialized lazily.
  • A normal startup also prints the Spring Boot banner, a Starting FirstAppApplication using Java ... with PID ... line, the active profiles, and a final Started FirstAppApplication in ... seconds line. They are switched off in this series' examples (with spring.main.banner-mode=off and spring.main.log-startup-info=false) because the startup line contains file paths of the machine the examples run on.

Running the generated test

Initializr's test class has a single, empty test method -
FirstAppApplicationTests.java generated by Spring Initializr
package com.javabrahman.firstapp;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class FirstAppApplicationTests {

	@Test
	void contextLoads() {
	}

}
It looks like it tests nothing, but @SpringBootTest starts the whole application context before the test runs. If any bean cannot be created - a missing dependency, a bad configuration value - the test fails. Running the tests with Maven (only the summary lines of Maven's output are shown) -  OUTPUT of mvn test (summary lines)
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 12.40 s -- in com.javabrahman.firstapp.FirstAppApplicationTests
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
The context-loading test passed. It is a cheap safety net worth keeping in every Spring Boot project; testing is covered in module 6.

Where to go from here

Summary

In this tutorial we first saw what Spring Initializr is and went through its options - build tool, language, Spring Boot version, metadata, packaging, Java version and dependencies - and generated a project from the website or with curl. We then walked through the generated project: its folders and Maven wrapper, its pom.xml with the Spring Boot parent and the web starter, and its main class. We added a REST controller, ran the application with spring-boot:run, called it with real HTTP requests, and read the startup log. Lastly, we ran the generated context-loading test and listed the next steps. The next tutorial explains what happened behind the scenes when the web server appeared without any configuration: how Spring Boot auto-configuration worksClick to read tutorial: How Spring Boot Auto-Configuration Works.