Spring Boot is a framework, built on top of Spring, that sets up a Java web application for you and runs it on a web server inside your own program, so you start it like any other Java program and it answers requests from a browser. This guide builds a small one step by step: a project from Spring Initializr, a web page, JSON from a record, a request parameter and a path variable, and one jar file that runs anywhere Java 25 does.
Here is the whole idea in one class. In a project generated as shown below, this file is a complete web endpoint:
package com.example.library; import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController; @RestControllerpublic class HelloController { @GetMapping("/hello") public String hello(@RequestParam(defaultValue = "world") String name) { return "Hello, " + name + "!"; }}$ curl "http://localhost:8080/hello?name=Ada"Hello, Ada!curl sends a request from the terminal and prints the answer; a browser at the same address shows the same text. Spring did the rest: it opened port 8080, read the request, chose this method because of @GetMapping("/hello"), filled name from ?name=Ada and sent the returned string back. The steps below build the project around it.
What you need#
A JDK 25 (Java 25, the current long-term support release) and either a terminal or an IDE such as IntelliJ IDEA, VS Code or Eclipse. Spring Boot 4 needs Java 17 or newer, and java -version tells you which one you have. You do not have to install Maven, the build tool: every generated project includes the Maven wrapper, a script named mvnw that downloads the right Maven version the first time you run it.
Create the project with Spring Initializr#
Spring Initializr, at start.spring.io, is the official project generator. You pick a few options on one page and download a ready project as a zip file:
- 1Project: Maven. Language: Java. Spring Boot: 4.1.1, or the newest 4.1 release it offers.
- 2Group
com.exampleand Artifactlibrary. The artifact names the project and its jar. Leave Packaging at Jar and choose Java 25. - 3Press Add dependencies and pick Spring Web and Thymeleaf. Spring Boot DevTools is optional: it restarts the application by itself whenever your compiled classes change.
- 4Press Generate, unzip the download, and open the folder in your IDE, or
cdinto it in a terminal.
Editors with Spring support can create the same project from their New Project screen. They ask the same questions and call the same service. Either way, the pom.xml asks for two starters:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>4.1.1</version></parent>...<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc</artifactId> </dependency> ...</dependencies><build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>A starter is one dependency that pulls in a set of libraries tested together. spring-boot-starter-webmvcis what Spring Web adds: Spring MVC (Spring's web framework), the Tomcat web server and Jackson, which turns Java objects into JSON. spring-boot-starter-thymeleaf adds Thymeleaf, a template engine that fills HTML pages with your data. The parent fixes the version of every library, so the dependencies list none, and the Spring Boot Maven plugin gives you the spring-boot:run command used below.
spring-boot-starter-web. Spring Boot 4 still has it but marks it deprecated in favor of spring-boot-starter-webmvc, which is what Initializr adds today.What is inside the generated project#
library/ mvnw, mvnw.cmd the Maven wrapper (macOS and Linux, Windows) pom.xml the build file src/main/java/com/example/library/ LibraryApplication.java the main class src/main/resources/ application.properties settings static/ files served as they are: CSS, images, scripts templates/ Thymeleaf pages src/test/java/com/example/library/ LibraryApplicationTests.javaThe only Java code is the main class:
package com.example.library; import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplicationpublic class LibraryApplication { public static void main(String[] args) { SpringApplication.run(LibraryApplication.class, args); } }An annotation such as @SpringBootApplicationis a label that a tool reads. This one tells Spring to search this class's package, and every package below it, for classes it should manage, such as your controllers, and to configure itself from the libraries it finds: Tomcat means a web server, Thymeleaf means HTML templates. SpringApplication.run does all of that and starts the server.
com.example.library and the packages below it. A controller placed in com.example.webis never found, so its address answers 404 Not Found. A browser then shows a page headed “Whitelabel Error Page” that says “This application has no explicit mapping for /error, so you are seeing this as a fallback.” Move the class into com.example.library, or a package under it such as com.example.library.web.Add a web page with @Controller and Thymeleaf#
A controller is a class whose methods answer requests. Add this one next to LibraryApplication.java:
package com.example.library; import java.util.List;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.GetMapping; @Controllerpublic class HomeController { @GetMapping("/") public String home(Model model) { model.addAttribute("title", "My library"); model.addAttribute("books", List.of("Dune", "Emma", "Solaris")); return "index"; }}@Controllermarks a class whose methods return the name of a page to show.@GetMapping("/")says this method answersGET /, the home page.- The
Modelholds the values the page may use. Spring passes one in, andaddAttributeputs a value in it under a name. - The return value
"index"is a template name. Thymeleaf looks forsrc/main/resources/templates/index.html.
Then create the template:
<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="UTF-8"> <title th:text="${title}">Library</title></head><body> <h1 th:text="${title}">Library</h1> <ul> <li th:each="book : ${books}" th:text="${book}">A book title</li> </ul></body></html>The th: attributes are Thymeleaf's. th:text="${title}"replaces the element's text with the model value title, and th:each repeats the <li>once for every book. The words inside the tags, such as “A book title”, only show if you open the file straight from disk. Served by Spring, the page has the heading “My library” and a list of Dune, Emma and Solaris.
Files in src/main/resources/static are served exactly as they are, with no controller: static/style.css is at http://localhost:8080/style.css, so the template can link to it.
Return JSON with @RestController#
A page is for people. Other programs, and the JavaScript in a page, want data, usually as JSON. A record describes one book, and an @RestController returns books instead of a page name:
// Book.javapackage com.example.library; public record Book(long id, String title, String author) {} // BookController.javapackage com.example.library; import java.util.List;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController; @RestControllerpublic class BookController { private final List<Book> books = List.of( new Book(1, "Dune", "Frank Herbert"), new Book(2, "Emma", "Jane Austen")); @GetMapping("/books") public List<Book> all() { return books; }}$ curl http://localhost:8080/books[{"id":1,"title":"Dune","author":"Frank Herbert"},{"id":2,"title":"Emma","author":"Jane Austen"}]Spring hands the list to Jackson, which writes each record as a JSON object with one field per component, in the order they are declared. A browser at http://localhost:8080/books shows the same text.
Key ideaAn @Controller method returns the name of a template, which Thymeleaf turns into HTML. An @RestController method returns the data itself, which Spring sends as text or JSON.
hello() from the top of this page sat in an @Controller, Spring would take Hello, Ada! as the name of a template, find no such file, and answer with an error page instead of the greeting. Methods that return text or data belong in an @RestController.Read request parameters and path variables#
A request can carry values in two places, and Spring has an annotation for each. The hello method above reads a request parameter, the part after ?. A path variable is a piece of the path itself, such as the 2 in /books/2. Add this method to BookController:
// new imports: org.springframework.http.ResponseEntity// and org.springframework.web.bind.annotation.PathVariable @GetMapping("/books/{id}")public ResponseEntity<Book> one(@PathVariable long id) { return books.stream() .filter(b -> b.id() == id) .findFirst() .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build());}$ curl http://localhost:8080/books/2{"id":2,"title":"Emma","author":"Jane Austen"}$ curl -i http://localhost:8080/books/9HTTP/1.1 404Content-Length: 0Date: Mon, 19 Oct 2026 12:55:00 GMT{id} in the mapping marks the variable part, and @PathVariable passes it to the parameter of the same name, converted to a long. ResponseEntity lets the method choose the status: ok(book) is 200 with the book, notFound() is 404 with no body. curl -i prints the status line, which is how you see the 404.
/hello?name=Ada/books/2@GetMapping("/hello")@GetMapping("/books/{id}")defaultValue. Without one, a missing value means 400 Bad Request; with defaultValue = "world", /hello answers Hello, world!/books/abc, means 400 Bad Request.Run the application and open localhost:8080#
From the project folder, start it through the wrapper (on Windows, mvnw.cmd spring-boot:run, or .\mvnw.cmd spring-boot:run in PowerShell). In an IDE, run the main method of LibraryApplication like any other program. The end of the log looks like this, from a real run; your dates, times and process id will differ:
$ ./mvnw spring-boot:run... :: Spring Boot :: (v4.1.1)...2026-10-19T15:54:52.707+03:00 INFO 59196 --- [library] [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)...2026-10-19T15:54:53.074+03:00 INFO 59196 --- [library] [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'2026-10-19T15:54:53.078+03:00 INFO 59196 --- [library] [ main] com.example.library.LibraryApplication : Started LibraryApplication in 0.975 seconds (process running for 1.165)“Tomcat started on port 8080” and “Started LibraryApplication” mean it is ready. Open http://localhost:8080 in a browser for the page, and http://localhost:8080/books for the JSON. The program does not end by itself: it waits for requests until you press Ctrl+C. After you change the code, stop it and start it again, or let DevTools restart it if you added it.
Change the port with server.port#
Settings live in src/main/resources/application.properties. The generated file has one line, and the [library] in every log line comes from it. Add a second:
spring.application.name=libraryserver.port=9090Restart, and the log says Tomcat started on port 9090: the page is now at http://localhost:9090.
server.port.Package the application as a runnable jar#
To run the application on a server, or send it to someone, build one jar file:
$ ./mvnw package...$ java -jar target/library-0.0.1-SNAPSHOT.jarpackage compiles the code, runs the tests and builds target/library-0.0.1-SNAPSHOT.jar (0.0.1-SNAPSHOT is the version Initializr writes into pom.xml). The Spring Boot Maven plugin then turns it into an executable jar that holds your classes and every library, Tomcat included. java -jar starts it with the same log and the same address on any computer with Java 25. Settings can also go on the command line: java -jar target/library-0.0.1-SNAPSHOT.jar --server.port=9090.
Where to go next: a database and a full REST API#
The books here live in a list, so they are fixed and vanish when the program stops. Three lessons take the same project further:
- Your first web API with Spring Boot builds a REST API for books: adding a book with POST and
@RequestBody, the 201 and 404 status codes, and how Spring hands a controller the objects it needs. - Saving data with Spring Data JPA stores the books in a database without a line of SQL.
- Maven and Gradle explains the
pom.xml, dependencies and the wrapper you used here.
Questions people ask#
- How do I create a web application in Spring Boot?
- Generate a project at start.spring.io with the Spring Web dependency (and Thymeleaf for HTML pages), add a class marked
@Controlleror@RestControllerwith a@GetMappingmethod, and start it with./mvnw spring-boot:run. The application then answers athttp://localhost:8080. - Which dependency makes a Spring Boot project a web application?
- The one Spring Initializr calls Spring Web. In Spring Boot 4 it adds
spring-boot-starter-webmvc, which brings Spring MVC, the embedded Tomcat server and Jackson for JSON. Spring Boot 3 projects usedspring-boot-starter-web, which Spring Boot 4 marks as deprecated. - Is Spring Boot still relevant in 2026?
- Yes. Spring Boot 4.1 is a 2026 release with open source support listed on spring.io until July 2027, and version 4.2 is scheduled for November 2026, so new versions keep arriving about every six months.
- Does Netflix use Spring Boot?
- Yes. In 2018 Netflix's engineering blog announced that Netflix was moving to Spring Boot as its core Java framework, and Netflix later open-sourced DGS, its framework for GraphQL services, which is built on Spring Boot.
- Is Spring Boot only for Java?
- Spring Boot is a Java framework, and you can also write a Spring Boot application in Kotlin or Groovy, the other two languages Spring Initializr offers. Whichever you choose, the application runs on the Java Virtual Machine.
- What port does a Spring Boot application use?
- Port 8080 by default, so it answers at
http://localhost:8080. Putserver.port=9090inapplication.properties, or start the jar with--server.port=9090, to use another port.