What is Spring Boot? Why Does It Matter?
Imagine you're building a house. The traditional way (plain Java) means you start from scratch - laying every brick, installing every pipe, wiring every socket yourself. Spring Boot is like buying a pre-fabricated house with electricity, plumbing, and walls already in place. You just add your furniture and move in.
Before Spring Boot, setting up a Java web application meant writing hundreds of lines of XML configuration, manually configuring databases, servers, security, and dependencies. It could take days just to get "Hello World" running. Spring Boot changed everything.
The Problem Spring Boot Solved
Traditional Spring Framework required:
- 100+ lines of XML configuration files
- Manual server setup and deployment
- Explicit bean definitions for every component
- Complex dependency management
- Hours of troubleshooting configuration issues
Spring Boot introduced "Convention over Configuration" - sensible defaults that work out of the box. Now you can go from zero to a running REST API in minutes.
Why Spring Boot? The Game Changer
1. Zero Configuration (Almost)
Spring Boot auto-configures your application based on dependencies you add. Need a database? Just add the dependency - Spring Boot configures it automatically.
2. Embedded Server
No need to install Tomcat or deploy WAR files. Spring Boot includes an embedded server. Run your app like any Java program: java -jar myapp.jar
3. Production-Ready Features
Built-in health checks, metrics, monitoring endpoints, and externalized configuration. Everything enterprises need is included.
4. Massive Ecosystem
Spring Boot has "starters" for everything - databases, security, messaging, caching, cloud services. Add one dependency, get full functionality.
5. Microservices Ready
Perfect for building microservices. Lightweight, standalone applications that start fast and consume minimal resources.
6. Industry Standard
Used by Netflix, Amazon, Uber, and thousands of enterprises. If you're doing Java in 2025, you're likely using Spring Boot.
Auto-Configuration: Spring Boot's Magic
Auto-configuration is Spring Boot's superpower. It looks at your classpath (the libraries you've included) and automatically configures beans based on what it finds.
How Auto-Configuration Works
Think of auto-configuration like a smart assistant who sees what you're working on and sets up your tools automatically.
// Traditional Spring: Manual configuration (XML or Java Config)
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/mydb");
ds.setUsername("user");
ds.setPassword("password");
return ds;
}
}
// Spring Boot: Just add dependency and properties
// application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=user
spring.datasource.password=password
// That's it! DataSource is auto-configured
@SpringBootApplication: The Magic Annotation
This single annotation combines three powerful annotations:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
// This annotation is actually three annotations combined:
// @Configuration - Makes this class a source of bean definitions
// @EnableAutoConfiguration - Enables auto-configuration magic
// @ComponentScan - Scans for components in this package and sub-packages
When Auto-Configuration Happens
- You add spring-boot-starter-web → Web server, REST controllers auto-configured
- You add spring-boot-starter-data-jpa → Database connection, repositories auto-configured
- You add spring-boot-starter-security → Security filters, authentication auto-configured
- You add H2 database → In-memory database auto-configured
Spring Boot is smart - it only configures what you need based on your dependencies.
Dependency Injection: The Heart of Spring
Dependency Injection (DI) is a fancy term for a simple concept: instead of creating objects yourself, Spring creates and manages them for you.
The Problem Without DI
// Without DI: Manual object creation (tightly coupled)
public class OrderService {
private EmailService emailService;
private PaymentService paymentService;
public OrderService() {
// Creating dependencies manually - hard to test and maintain
this.emailService = new EmailService();
this.paymentService = new PaymentService();
}
public void placeOrder(Order order) {
paymentService.processPayment(order);
emailService.sendConfirmation(order);
}
}
// Problem: How do you test this? How do you swap EmailService with a different implementation?
The Solution: Dependency Injection
// With DI: Spring manages object creation
@Service
public class OrderService {
private final EmailService emailService;
private final PaymentService paymentService;
// Constructor injection (recommended)
@Autowired
public OrderService(EmailService emailService, PaymentService paymentService) {
this.emailService = emailService;
this.paymentService = paymentService;
}
public void placeOrder(Order order) {
paymentService.processPayment(order);
emailService.sendConfirmation(order);
}
}
// Spring creates EmailService and PaymentService, then injects them
// Easy to test - just pass mock objects
// Easy to swap implementations - change one line in configuration
Types of Dependency Injection
1. Constructor Injection (Recommended)
@Service
public class UserService {
private final UserRepository userRepository;
// Best practice: Constructor injection with final fields
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// Benefits:
// - Immutable (final fields)
// - Required dependencies are clear
// - Easy to test
// - @Autowired is optional in Spring Boot with single constructor
2. Field Injection (Simple, but not recommended)
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
// Simpler syntax, but harder to test
}
// Drawbacks:
// - Can't make fields final (mutable)
// - Harder to write unit tests
// - Hidden dependencies
3. Setter Injection (For optional dependencies)
@Service
public class NotificationService {
private EmailSender emailSender;
@Autowired(required = false) // Optional dependency
public void setEmailSender(EmailSender emailSender) {
this.emailSender = emailSender;
}
}
// Use when dependency is optional
Which Injection Type to Use?
- Constructor Injection - 95% of the time (required dependencies)
- Setter Injection - Optional dependencies
- Field Injection - Quick prototypes only (avoid in production)
Key Spring Boot Annotations
Annotations are like labels that tell Spring how to handle your classes. Think of them as instructions for Spring's automated system.
Core Stereotypes
// @Component: Generic Spring-managed component
@Component
public class EmailValidator {
public boolean isValid(String email) {
return email.contains("@");
}
}
// @Service: Business logic layer (same as @Component, but clearer intent)
@Service
public class OrderService {
public void processOrder(Order order) {
// Business logic here
}
}
// @Repository: Data access layer (adds database exception translation)
@Repository
public class UserRepository {
public User findById(Long id) {
// Database operations
}
}
// @Controller: Web layer (handles HTTP requests, returns views)
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
return "index"; // Returns view name
}
}
// @RestController: REST API layer (@Controller + @ResponseBody)
@RestController
public class UserApiController {
@GetMapping("/api/users")
public List<User> getUsers() {
return userService.findAll(); // Returns JSON automatically
}
}
Configuration Annotations
// @Configuration: Defines a configuration class
@Configuration
public class AppConfig {
// @Bean: Manually create and configure beans
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
// @Value: Inject values from properties files
@Service
public class EmailService {
@Value("${app.email.from}")
private String fromEmail;
@Value("${app.email.enabled:true}") // Default value: true
private boolean emailEnabled;
}
// @ConfigurationProperties: Type-safe configuration
@ConfigurationProperties(prefix = "app.email")
public class EmailProperties {
private String from;
private String host;
private int port;
// Getters and setters
}
Web Annotations
@RestController
@RequestMapping("/api/products")
public class ProductController {
// GET request: /api/products
@GetMapping
public List<Product> getAllProducts() {
return productService.findAll();
}
// GET request: /api/products/123
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.findById(id);
}
// POST request: Create new product
@PostMapping
public Product createProduct(@RequestBody Product product) {
return productService.save(product);
}
// PUT request: Update product
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product product) {
return productService.update(id, product);
}
// DELETE request: Delete product
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.delete(id);
}
// Query parameters: /api/products/search?name=laptop
@GetMapping("/search")
public List<Product> search(@RequestParam String name) {
return productService.searchByName(name);
}
}
Quick Reference: Which Annotation?
- @Service - Business logic (OrderService, UserService)
- @Repository - Database access (UserRepository)
- @RestController - REST APIs (return JSON/XML)
- @Controller - Web pages (return HTML views)
- @Component - Everything else (utilities, helpers)
- @Configuration - Configuration classes
Spring Boot Starters: Pre-Packaged Dependencies
Starters are like combo meals at a restaurant. Instead of ordering each item separately, you get everything you need in one package.
What Are Starters?
A starter is a single dependency that brings in all libraries needed for a specific feature. No need to hunt for compatible versions or figure out which libraries work together.
<!-- Without Starter: Manual dependency management -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
<!-- ...and 15 more dependencies -->
<!-- With Starter: One dependency includes everything -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Essential Starters
<!-- Web Applications (REST APIs, web apps) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Includes: Tomcat, Spring MVC, Jackson JSON, validation -->
<!-- Database Access (JPA/Hibernate) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Includes: Hibernate, JPA, Spring Data JPA, JDBC -->
<!-- Security (Authentication, Authorization) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Includes: Spring Security core, web, config -->
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- Includes: JUnit, Mockito, AssertJ, Spring Test -->
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Includes: Hibernate Validator, Bean Validation API -->
<!-- Caching -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Email -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
Complete Application Example
// pom.xml - Just add starters you need
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
// application.properties - Simple configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.show-sql=true
// Main Application Class
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
// Entity
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters and setters
}
// Repository (no implementation needed!)
public interface UserRepository extends JpaRepository<User, Long> {
// Spring Data JPA generates implementation
List<User> findByName(String name);
}
// Service
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(User user) {
return userRepository.save(user);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
}
// REST Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
@GetMapping
public List<User> getUsers() {
return userService.getAllUsers();
}
}
// That's it! You have a fully functional REST API with database
// Run the app and test: POST http://localhost:8080/api/users
When to Use Spring Boot?
Perfect For:
- REST APIs - Build production-ready APIs in minutes
- Microservices - Lightweight, standalone services
- Enterprise Applications - Complex business logic with database
- Web Applications - Full-stack web apps with server-side rendering
- Data Processing - Batch jobs, scheduled tasks
- Cloud Applications - Deploy to AWS, Azure, Google Cloud
Not Ideal For:
- Simple Scripts - Overhead is too much for tiny utilities
- Mobile Apps - Use backend for APIs, not mobile UI
- Real-time Gaming - Too much overhead, use lower-level frameworks
- Embedded Systems - Memory footprint too large
Best Practices
1. Use Constructor Injection
// GOOD
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
// AVOID
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}
2. Externalize Configuration
// application.properties
app.payment.api-key=${PAYMENT_API_KEY}
app.payment.timeout=5000
// Use @ConfigurationProperties for type safety
@ConfigurationProperties(prefix = "app.payment")
public class PaymentConfig {
private String apiKey;
private int timeout;
// Getters and setters
}
3. Use Profiles for Environments
// application-dev.properties spring.datasource.url=jdbc:h2:mem:testdb // application-prod.properties spring.datasource.url=jdbc:postgresql://prod-db:5432/mydb // Activate with: java -jar app.jar --spring.profiles.active=prod
4. Use DTOs for API Responses
// Don't expose entities directly
@GetMapping("/users/{id}")
public UserDTO getUser(@PathVariable Long id) {
User user = userService.findById(id);
return new UserDTO(user); // Convert to DTO
}
// DTO filters sensitive data and controls API contract
public class UserDTO {
private Long id;
private String name;
// No password, no internal fields
}
5. Use Proper Exception Handling
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
ErrorResponse error = new ErrorResponse("ERROR", "Something went wrong");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
6. Enable Actuator for Monitoring
<!-- Add actuator dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.properties
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always
# Access: http://localhost:8080/actuator/health