
Spring Boot is the fastest way to build production-ready Java applications. It takes the powerful Spring Framework and removes the configuration headaches. You can go from zero to a running web application in minutes.
Before Spring Boot, setting up a Spring project meant writing dozens of XML configuration files. Spring Boot eliminates most of that. It auto-configures everything sensibly, embeds a web server, and lets you focus on your actual code.
This tutorial covers the basics: creating a project, building REST APIs, connecting to databases, and understanding how Spring Boot works under the hood.
What Spring Boot Provides
Spring Boot builds on the Spring Framework but adds several conveniences:
Auto-configuration: Spring Boot examines your classpath and configures beans automatically. Add a database driver, and it configures a DataSource. Add Spring Web, and it configures an embedded Tomcat server.
Starter dependencies: Instead of hunting for compatible library versions, you add a single starter. spring-boot-starter-web pulls in everything needed for web development.
Embedded server: No need to install Tomcat or deploy WAR files. Spring Boot embeds Tomcat, Jetty, or Undertow directly in your JAR.
Production-ready features: Health checks, metrics, and externalized configuration come built in.
Creating a Spring Boot Project
The easiest way to start is Spring Initializr at start.spring.io. It generates a project skeleton with your chosen dependencies.
Select these options:
- Project: Maven
- Language: Java
- Spring Boot: 3.2.x (latest stable)
- Group: com.example
- Artifact: demo
- Packaging: Jar
- Java: 21
Add the “Spring Web” dependency for building REST APIs. Click Generate to download a zip file.
Extract and open the project in your IDE. The structure looks like this:
demo/
src/
main/
java/
com/example/demo/
DemoApplication.java
resources/
application.properties
static/
templates/
test/
java/
com/example/demo/
DemoApplicationTests.java
pom.xml
The Main Application Class
DemoApplication.java is the entry point:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
The @SpringBootApplication annotation combines three things:
- @Configuration: This class can define beans
- @EnableAutoConfiguration: Turn on auto-configuration
- @ComponentScan: Scan for components in this package and below
Run the main method. Spring Boot starts an embedded Tomcat server on port 8080. You’ll see log output ending with “Started DemoApplication.”
Building a REST API
REST APIs are Spring Boot’s sweet spot. Create a controller to handle HTTP requests:
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}
}
@RestController marks this class as a web controller where every method returns data (not a view). @GetMapping maps HTTP GET requests to the method.
Run the application and visit http://localhost:8080/hello in your browser. You’ll see “Hello, World!”
Returning JSON
Spring Boot automatically converts objects to JSON. Create a simple class:
package com.example.demo;
public class User {
private Long id;
private String name;
private String email;
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters required for JSON serialization
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
}
Return it from a controller method:
@RestController
public class UserController {
@GetMapping("/user")
public User getUser() {
return new User(1L, "Alice", "alice@example.com");
}
}
Visit http://localhost:8080/user and you get JSON:
{"id":1,"name":"Alice","email":"alice@example.com"}
Spring Boot includes Jackson for JSON processing. It serializes your objects automatically based on their getter methods.
Path Variables and Request Parameters
Capture values from the URL:
@RestController
@RequestMapping("/users")
public class UserController {
// GET /users/5
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
// In reality, fetch from database
return new User(id, "User " + id, "user" + id + "@example.com");
}
// GET /users?name=Alice
@GetMapping
public List<User> searchUsers(@RequestParam(required = false) String name) {
// Filter users by name
return List.of(
new User(1L, "Alice", "alice@example.com"),
new User(2L, "Bob", "bob@example.com")
);
}
}
@PathVariable extracts values from the URL path. @RequestParam extracts query string parameters.
Handling POST Requests
Accept JSON in the request body:
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public User createUser(@RequestBody User user) {
// Save to database, assign ID
System.out.println("Creating user: " + user.getName());
return new User(99L, user.getName(), user.getEmail());
}
}
@RequestBody tells Spring to deserialize the JSON request body into a User object. Test with curl:
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"charlie@example.com"}'
Full CRUD Example
Here’s a complete controller with all HTTP methods:
@RestController
@RequestMapping("/api/users")
public class UserController {
private Map<Long, User> users = new HashMap<>();
private Long nextId = 1L;
@GetMapping
public List<User> getAllUsers() {
return new ArrayList<>(users.values());
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
User user = users.get(id);
if (user == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
}
return user;
}
@PostMapping
public User createUser(@RequestBody User user) {
user = new User(nextId++, user.getName(), user.getEmail());
users.put(user.getId(), user);
return user;
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
if (!users.containsKey(id)) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
}
user = new User(id, user.getName(), user.getEmail());
users.put(id, user);
return user;
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
if (users.remove(id) == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
}
}
}
Dependency Injection
Spring manages object creation and wiring through dependency injection. Instead of creating objects yourself, you declare dependencies and Spring provides them.
// Service class - business logic
@Service
public class UserService {
public User findById(Long id) {
// Database lookup would go here
return new User(id, "User " + id, "user@example.com");
}
public List<User> findAll() {
return List.of(
new User(1L, "Alice", "alice@example.com"),
new User(2L, "Bob", "bob@example.com")
);
}
}
// Controller depends on service
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
// Constructor injection - Spring provides the UserService
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
}
@Service marks UserService as a Spring-managed component. When Spring creates the UserController, it sees the constructor needs a UserService and injects one automatically.
This pattern makes code testable. In tests, you can provide a mock UserService instead of a real one.
Configuration with application.properties
The application.properties file (or application.yml) configures your application:
# Server configuration
server.port=8081
# Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
# Logging
logging.level.com.example=DEBUG
# Custom properties
app.name=My Application
app.version=1.0
Access custom properties in your code:
@RestController
public class InfoController {
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
@GetMapping("/info")
public Map<String, String> getInfo() {
return Map.of(
"name", appName,
"version", appVersion
);
}
}
Profiles
Different configurations for different environments:
application.properties # Default
application-dev.properties # Development
application-prod.properties # Production
Activate a profile:
# Command line
java -jar app.jar --spring.profiles.active=prod
# Or in application.properties
spring.profiles.active=dev
Connecting to a Database
Spring Data JPA simplifies database access dramatically. Add these dependencies to your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Configure H2 (an in-memory database, good for development):
# application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create-drop
Entity Class
Mark your class as a JPA entity:
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(unique = true)
private String email;
// Default constructor required by JPA
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
// Getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
Repository Interface
Create an interface extending JpaRepository. Spring generates the implementation:
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
// Spring generates these automatically based on method names
List<User> findByName(String name);
User findByEmail(String email);
List<User> findByNameContaining(String namePart);
}
JpaRepository provides findAll(), findById(), save(), delete(), and more. Spring Data JPA parses method names like findByEmail and generates the query automatically.
Using the Repository
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<User> findAll() {
return userRepository.findAll();
}
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
public User save(User user) {
return userRepository.save(user);
}
public void delete(Long id) {
userRepository.deleteById(id);
}
}
Error Handling
Handle exceptions globally with @ControllerAdvice:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Map<String, String>> handleRuntimeException(RuntimeException ex) {
Map<String, String> error = Map.of(
"error", ex.getMessage(),
"status", "500"
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
@ExceptionHandler(ResponseStatusException.class)
public ResponseEntity<Map<String, String>> handleResponseStatusException(ResponseStatusException ex) {
Map<String, String> error = Map.of(
"error", ex.getReason(),
"status", String.valueOf(ex.getStatusCode().value())
);
return ResponseEntity.status(ex.getStatusCode()).body(error);
}
}
Running and Packaging
During development, run from your IDE or use Maven:
mvn spring-boot:run
For production, package as an executable JAR:
mvn clean package
This creates target/demo-0.0.1-SNAPSHOT.jar. Run it directly:
java -jar target/demo-0.0.1-SNAPSHOT.jar
The JAR contains your code, all dependencies, and an embedded Tomcat server. Deploy it anywhere Java runs.
Testing
Spring Boot includes testing support. Test your REST API:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnHello() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("Hello, World!"));
}
@Test
void shouldReturnUser() throws Exception {
mockMvc.perform(get("/user"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Alice"));
}
}
Run tests with:
mvn test
Common Starters
Spring Boot starters bundle related dependencies:
spring-boot-starter-web: REST APIs, Spring MVC, embedded Tomcat
spring-boot-starter-data-jpa: JPA, Hibernate, database access
spring-boot-starter-security: Authentication and authorization
spring-boot-starter-test: JUnit, Mockito, testing utilities
spring-boot-starter-validation: Bean validation with annotations
spring-boot-starter-actuator: Health checks, metrics, monitoring endpoints
Add them to your pom.xml as needed. Each starter pulls in sensible defaults that work together.
Project Structure Best Practices
As projects grow, organize by feature or layer:
com.example.demo/
DemoApplication.java
user/
User.java
UserController.java
UserService.java
UserRepository.java
product/
Product.java
ProductController.java
ProductService.java
ProductRepository.java
config/
SecurityConfig.java
WebConfig.java
exception/
GlobalExceptionHandler.java
ResourceNotFoundException.java
Spring’s component scanning finds @Controller, @Service, @Repository, and @Component annotations anywhere under the main application package.
Next Steps
This tutorial covered the fundamentals. Spring Boot offers much more:
Spring Security for authentication and authorization. Protect endpoints, integrate with OAuth2, manage user sessions.
Spring Data for various databases: MongoDB, Redis, Elasticsearch, and more.
Spring Cloud for microservices: service discovery, configuration management, circuit breakers.
Spring WebFlux for reactive, non-blocking applications.
The official documentation at docs.spring.io covers everything in depth. The Spring Guides at spring.io/guides provide focused tutorials on specific topics.
Previous: Introduction to Maven
Related: Introduction to JDBC | Introduction to Object-Oriented Programming | Java Collections Framework Overview
Sources
- Spring. “Spring Boot Reference Documentation.” docs.spring.io/spring-boot
- Spring. “Spring Initializr.” start.spring.io
- Spring. “Spring Guides.” spring.io/guides
- Walls, Craig. “Spring Boot in Action.” Manning, 2016


