What is Spring Boot?
Think of Spring Boot like a pre-configured development kit:
- Spring Framework: Like buying all the parts to build a PC
- Spring Boot: Like buying a pre-built PC that just works
Spring Boot is an opinionated framework that makes it easy to create stand-alone, production-grade Spring applications. It provides:
- Auto-configuration: Automatically configures your application based on dependencies
- Embedded servers: No need to deploy to external servers (Tomcat, Jetty, Undertow)
- Starter dependencies: Curated dependency bundles for common use cases
- Production-ready features: Metrics, health checks, externalized configuration
| Feature | Traditional Spring | Spring Boot |
|---|---|---|
| Configuration | Manual XML or Java config | Auto-configuration |
| Dependencies | Manage each individually | Starters bundle common deps |
| Server | Deploy to external server | Embedded server included |
| Packaging | WAR file | Executable JAR |
| Setup Time | Hours of configuration | Minutes to running app |
Creating a Spring Boot Application
1. Using Spring Initializr
The easiest way: Visit start.spring.io
2. Maven Project Structure
my-app/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/myapp/
│ │ │ ├── MyAppApplication.java // Main class
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ ├── repository/
│ │ │ └── model/
│ │ └── resources/
│ │ ├── application.properties // Configuration
│ │ ├── static/ // Static files (CSS, JS)
│ │ └── templates/ // View templates
│ └── test/
│ └── java/
│ └── com/example/myapp/
└── target/
3. pom.xml with Spring Boot Parent
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<!-- Spring Boot Parent - manages versions -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<!-- Web application starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Testing starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-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>
The Main Application Class
package com.example.myapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // The magic annotation!
public class MyAppApplication {
public static void main(String[] args) {
// Starts the application
SpringApplication.run(MyAppApplication.class, args);
}
}
// @SpringBootApplication is a shortcut for:
@Configuration // This class provides beans
@EnableAutoConfiguration // Auto-configure based on classpath
@ComponentScan // Scan for components in this package and below
Running the Application
# Using Maven
mvn spring-boot:run
# Or build and run the JAR
mvn clean package
java -jar target/my-app-1.0.0.jar
# With specific profile
java -jar my-app.jar --spring.profiles.active=prod
# With system properties
java -Dserver.port=9090 -jar my-app.jar
Auto-Configuration
Spring Boot automatically configures your application based on what's on the classpath:
// If you add spring-boot-starter-web:
// - Embedded Tomcat server is configured
// - DispatcherServlet is registered
// - Jackson for JSON serialization
// If you add spring-boot-starter-data-jpa + H2:
// - DataSource is auto-configured
// - EntityManagerFactory is created
// - Transaction manager is set up
// You can disable specific auto-configurations:
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
SecurityAutoConfiguration.class
})
public class MyApp { }
Viewing Auto-Configuration Report
# Run with debug to see what was auto-configured
java -jar my-app.jar --debug
# Or in application.properties:
debug=true
Building a Simple REST API
Model
package com.example.myapp.model;
public class User {
private Long id;
private String name;
private String email;
// Constructors
public User() {}
public User(Long id, String name, String email) {
this.id = id;
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; }
}
Service
package com.example.myapp.service;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class UserService {
private Map<Long, User> users = new HashMap<>();
private Long nextId = 1L;
public List<User> findAll() {
return new ArrayList<>(users.values());
}
public Optional<User> findById(Long id) {
return Optional.ofNullable(users.get(id));
}
public User save(User user) {
if (user.getId() == null) {
user.setId(nextId++);
}
users.put(user.getId(), user);
return user;
}
public void deleteById(Long id) {
users.remove(id);
}
}
Controller
package com.example.myapp.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
// Constructor injection (recommended)
public UserController(UserService userService) {
this.userService = userService;
}
// GET /api/users
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// GET /api/users/123
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST /api/users
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@RequestBody User user) {
return userService.save(user);
}
// PUT /api/users/123
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@RequestBody User user) {
return userService.findById(id)
.map(existing -> {
user.setId(id);
return ResponseEntity.ok(userService.save(user));
})
.orElse(ResponseEntity.notFound().build());
}
// DELETE /api/users/123
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteUser(@PathVariable Long id) {
userService.deleteById(id);
}
}
Application Properties
# src/main/resources/application.properties
# Server configuration
server.port=8080
server.servlet.context-path=/myapp
# Logging
logging.level.root=INFO
logging.level.com.example=DEBUG
logging.file.name=app.log
# Database (H2 example)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
# JPA
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# Custom properties
app.name=My Application
app.version=1.0.0
YAML Alternative
# src/main/resources/application.yml
server:
port: 8080
servlet:
context-path: /myapp
spring:
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
logging:
level:
root: INFO
com.example: DEBUG
app:
name: My Application
version: "1.0.0"
Accessing Properties
@Component
public class AppConfig {
// Direct injection
@Value("${app.name}")
private String appName;
@Value("${app.version:1.0}") // With default value
private String version;
@Value("${server.port}")
private int serverPort;
}
// Or use @ConfigurationProperties for groups
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String version;
// Getters and setters
}
DevTools for Development
<!-- Add to pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
DevTools provides:
- Automatic restart: App restarts when code changes
- LiveReload: Browser refreshes automatically
- Sensible defaults: Disables caching for development
- H2 console: Enabled by default
Actuator (Production Monitoring)
<!-- Add to pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.properties
management.endpoints.web.exposure.include=health,info,metrics,env
management.endpoint.health.show-details=always
Actuator Endpoints:
/actuator/health- Application health status/actuator/info- Application information/actuator/metrics- Application metrics/actuator/env- Environment properties/actuator/beans- All Spring beans/actuator/mappings- All @RequestMapping paths
Best Practices
DO:
- Use constructor injection - More testable, final fields
- Follow layered architecture - Controller > Service > Repository
- Externalize configuration - Use application.properties
- Use profiles - Different configs for dev, test, prod
- Enable Actuator - For monitoring and health checks
- Use starters - They manage compatible versions
- Write tests - Use spring-boot-starter-test
DON'T:
- Don't use field injection - Harder to test
- Don't hardcode configuration - Use properties
- Don't ignore auto-configuration - Understand what it does
- Don't deploy as WAR unless necessary - JAR is simpler
- Don't mix configuration styles - Pick one (properties or YAML)
Summary
- Spring Boot: Opinionated, rapid application development
- @SpringBootApplication: Main annotation, enables auto-configuration
- Auto-configuration: Automatic setup based on classpath
- Starters: Curated dependency bundles
- Embedded server: Tomcat, Jetty, or Undertow included
- application.properties: Externalized configuration
- DevTools: Development-time features
- Actuator: Production monitoring endpoints
- Executable JAR: java -jar app.jar to run