Spring Boot Framework
Develop enterprise applications rapidly
Spring Boot Framework
Master the Spring Boot Framework with free flashcards and spaced repetition practice. This lesson covers auto-configuration, dependency injection, RESTful web services, Spring Data JPA, and Spring Securityβessential concepts for building modern Java enterprise applications quickly and efficiently.
Welcome to Spring Boot π»
Spring Boot is a powerful framework that simplifies the creation of production-ready Spring applications. Built on top of the Spring Framework, it eliminates the need for extensive XML configuration and boilerplate code, allowing developers to focus on writing business logic rather than wrestling with setup.
π€ Did you know? Spring Boot was first released in April 2014, and it revolutionized Java development by dramatically reducing the time needed to bootstrap a Spring application from hours to minutes!
Core Concepts
1. Auto-Configuration π§
Auto-configuration is Spring Boot's killer feature. It automatically configures your application based on the dependencies present in your classpath. When you add a database driver, Spring Boot automatically configures a DataSource. Add a web dependency? You get an embedded Tomcat server configured and ready to go.
π‘ How it works: Spring Boot uses @Conditional annotations to detect what's on your classpath and configures beans accordingly. The @SpringBootApplication annotation combines three key annotations:
@Configuration: Tags the class as a source of bean definitions@EnableAutoConfiguration: Enables auto-configuration magic@ComponentScan: Scans for components in the current package and sub-packages
| Traditional Spring | Spring Boot |
|---|---|
| XML configuration files | Convention over configuration |
| Manual bean wiring | Auto-configuration |
| External server deployment | Embedded server (Tomcat/Jetty) |
| Complex setup | Starter dependencies |
2. Dependency Injection and Inversion of Control (IoC) π
Dependency Injection (DI) is the core principle of Spring. Instead of objects creating their dependencies, Spring's IoC container injects them. This promotes loose coupling and testability.
Three types of dependency injection:
- Constructor Injection (recommended):
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
}
- Setter Injection:
public class UserService {
private UserRepository repository;
@Autowired
public void setRepository(UserRepository repository) {
this.repository = repository;
}
}
- Field Injection (not recommended):
public class UserService {
@Autowired
private UserRepository repository;
}
π§ Memory device: Think of DI as a "Hollywood Principle" - "Don't call us, we'll call you." Your objects don't create dependencies; Spring calls your constructor with everything you need.
3. Spring Boot Starters π¦
Starters are pre-configured dependency descriptors that bring in everything you need for a particular feature. They follow the naming convention spring-boot-starter-*.
| Starter | Purpose | Key Dependencies |
|---|---|---|
| spring-boot-starter-web | Build web applications | Spring MVC, Tomcat, Jackson |
| spring-boot-starter-data-jpa | Database access with JPA | Hibernate, Spring Data JPA |
| spring-boot-starter-security | Authentication & authorization | Spring Security |
| spring-boot-starter-test | Testing support | JUnit, Mockito, AssertJ |
| spring-boot-starter-actuator | Production monitoring | Health checks, metrics |
4. Application Properties Configuration βοΈ
Spring Boot uses application.properties or application.yml for configuration. These files control everything from server ports to database connections.
application.properties example:
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
spring.jpa.hibernate.ddl-auto=update
application.yml equivalent:
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: secret
jpa:
hibernate:
ddl-auto: update
π‘ Tip: Use profiles for different environments! Create application-dev.properties, application-prod.properties, and activate with spring.profiles.active=dev.
5. RESTful Web Services with Spring MVC π
Spring Boot makes creating REST APIs incredibly simple using @RestController and mapping annotations.
Key annotations:
@RestController: Combines@Controllerand@ResponseBody@RequestMapping: Maps HTTP requests to handler methods@GetMapping,@PostMapping,@PutMapping,@DeleteMapping: Shorthand for specific HTTP methods@PathVariable: Extracts values from URI paths@RequestBody: Maps request body to Java object@RequestParam: Extracts query parameters
6. Spring Data JPA ποΈ
Spring Data JPA dramatically simplifies database access by providing repository interfaces. You define an interface, and Spring generates the implementation automatically!
Repository hierarchy:
Repository
β
β
CrudRepository
(save, findById, findAll, delete)
β
β
PagingAndSortingRepository
(pagination & sorting)
β
β
JpaRepository
(JPA-specific extensions)
Entity relationships:
@OneToOne: One entity relates to one other@OneToMany: One entity has many related entities@ManyToOne: Many entities relate to one@ManyToMany: Many entities relate to many
7. Exception Handling π¨
Spring Boot provides elegant exception handling using @ControllerAdvice and @ExceptionHandler.
Best practices:
- Create custom exception classes
- Use
@ControllerAdvicefor global exception handling - Return appropriate HTTP status codes
- Provide meaningful error messages
8. Spring Boot Actuator π
Actuator provides production-ready features like health checks, metrics, and monitoring endpoints.
Common endpoints:
| Endpoint | Purpose |
|---|---|
| /actuator/health | Application health status |
| /actuator/info | Application information |
| /actuator/metrics | Application metrics |
| /actuator/env | Environment properties |
| /actuator/loggers | Logger configuration |
Detailed Examples
Example 1: Complete REST API with Spring Boot π―
Let's build a complete book management system:
Step 1: Entity class
import javax.persistence.*;
import lombok.Data;
@Entity
@Table(name = "books")
@Data
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String author;
@Column(nullable = false)
private String isbn;
private Double price;
}
Explanation: The @Entity annotation marks this as a JPA entity. @Data is a Lombok annotation that generates getters, setters, toString, equals, and hashCode. The @GeneratedValue ensures auto-incrementing IDs.
Step 2: Repository interface
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByAuthor(String author);
List<Book> findByTitleContaining(String keyword);
Book findByIsbn(String isbn);
}
Explanation: By extending JpaRepository, we get CRUD operations for free! Spring Data JPA generates query implementations from method names automatically. findByAuthor becomes SELECT * FROM books WHERE author = ?.
Step 3: Service layer
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class BookService {
private final BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public List<Book> getAllBooks() {
return bookRepository.findAll();
}
public Book getBookById(Long id) {
return bookRepository.findById(id)
.orElseThrow(() -> new BookNotFoundException(id));
}
public Book saveBook(Book book) {
return bookRepository.save(book);
}
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}
Explanation: The @Service annotation marks this as a Spring service component. @Transactional ensures database operations are wrapped in transactions. Constructor injection provides the repository dependency.
Step 4: REST Controller
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public List<Book> getAllBooks() {
return bookService.getAllBooks();
}
@GetMapping("/{id}")
public ResponseEntity<Book> getBookById(@PathVariable Long id) {
Book book = bookService.getBookById(id);
return ResponseEntity.ok(book);
}
@PostMapping
public ResponseEntity<Book> createBook(@RequestBody Book book) {
Book savedBook = bookService.saveBook(book);
return ResponseEntity.status(HttpStatus.CREATED).body(savedBook);
}
@PutMapping("/{id}")
public ResponseEntity<Book> updateBook(@PathVariable Long id,
@RequestBody Book book) {
book.setId(id);
Book updatedBook = bookService.saveBook(book);
return ResponseEntity.ok(updatedBook);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
bookService.deleteBook(id);
return ResponseEntity.noContent().build();
}
}
Explanation: This controller handles all CRUD operations through RESTful endpoints. @PathVariable extracts the {id} from URLs like /api/books/5. @RequestBody automatically deserializes JSON to Book objects.
Example 2: Custom Exception Handling π‘οΈ
Custom exception:
public class BookNotFoundException extends RuntimeException {
public BookNotFoundException(Long id) {
super("Book not found with id: " + id);
}
}
Error response DTO:
import lombok.AllArgsConstructor;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
public class ErrorResponse {
private LocalDateTime timestamp;
private int status;
private String error;
private String message;
private String path;
}
Global exception handler:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import java.time.LocalDateTime;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BookNotFoundException.class)
public ResponseEntity<ErrorResponse> handleBookNotFound(
BookNotFoundException ex, WebRequest request) {
ErrorResponse error = new ErrorResponse(
LocalDateTime.now(),
HttpStatus.NOT_FOUND.value(),
"Not Found",
ex.getMessage(),
request.getDescription(false)
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(
Exception ex, WebRequest request) {
ErrorResponse error = new ErrorResponse(
LocalDateTime.now(),
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Internal Server Error",
ex.getMessage(),
request.getDescription(false)
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Explanation: @ControllerAdvice applies exception handling globally across all controllers. When BookNotFoundException is thrown anywhere, this handler catches it and returns a structured error response with appropriate HTTP status.
Example 3: Application Properties and Profiles π§
application.yml (base configuration):
spring:
application:
name: book-service
jpa:
show-sql: true
hibernate:
ddl-auto: validate
properties:
hibernate:
format_sql: true
server:
port: 8080
management:
endpoints:
web:
exposure:
include: health,info,metrics
application-dev.yml (development profile):
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: create-drop
logging:
level:
root: INFO
com.example: DEBUG
application-prod.yml (production profile):
spring:
datasource:
url: jdbc:postgresql://prod-db:5432/bookdb
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 10
jpa:
hibernate:
ddl-auto: validate
logging:
level:
root: WARN
com.example: INFO
file:
name: /var/log/book-service.log
Explanation: Profiles allow environment-specific configuration. Development uses an in-memory H2 database with debug logging, while production uses PostgreSQL with environment variables for credentials and minimal logging.
Example 4: Security Configuration π
Add Spring Security starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Security configuration:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/books/**").hasRole("USER")
.requestMatchers("/actuator/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Explanation: This configuration secures endpoints based on roles. The /api/books/** endpoints require USER role, while /actuator/** requires ADMIN role. Passwords are encrypted using BCrypt. In production, you'd use a database-backed UserDetailsService.
Common Mistakes β οΈ
Using field injection instead of constructor injection
- β
@Autowired private UserService service; - β Use constructor injection for better testability and immutability
- β
Forgetting @Repository, @Service, or @Controller annotations
- Spring won't detect your components without these stereotype annotations
- Remember:
@RestController=@Controller+@ResponseBody
Not handling exceptions properly
- Don't let exceptions bubble up as generic 500 errors
- Always use
@ControllerAdvicefor global exception handling
Mixing business logic in controllers
- Controllers should be thin, delegating to service layer
- Keep the separation: Controller β Service β Repository
Hardcoding configuration values
- β
private String dbUrl = "localhost:3306"; - β
Use
@Value("${spring.datasource.url}")or@ConfigurationProperties
- β
Not using Spring Boot Starters
- Don't manually add individual dependencies
- Use starters to get compatible, tested dependency sets
Ignoring database connection pooling
- Spring Boot configures HikariCP by default
- Adjust pool size for production:
spring.datasource.hikari.maximum-pool-size
Using @Transactional incorrectly
- Place on service methods, not controller methods
- Understand propagation and isolation levels
Exposing all Actuator endpoints in production
- Limit exposed endpoints:
management.endpoints.web.exposure.include=health,info - Secure actuator endpoints with Spring Security
- Limit exposed endpoints:
Not testing your application
- Use
@SpringBootTestfor integration tests - Use
@WebMvcTestfor controller tests - Mock dependencies with
@MockBean
- Use
Key Takeaways π―
β Spring Boot eliminates boilerplate through auto-configuration and sensible defaults
β Dependency injection promotes loose coupling - prefer constructor injection
β
Starters bring in curated dependencies - use spring-boot-starter-* for features
β Profiles enable environment-specific configuration - dev, test, prod settings
β REST APIs are simple with @RestController - automatic JSON serialization
β Spring Data JPA eliminates DAO code - repository interfaces generate implementations
β
Exception handling should be centralized - use @ControllerAdvice
β Actuator provides production monitoring - health checks, metrics, info endpoints
β Security is configurable and powerful - use Spring Security for authentication/authorization
β Follow the layered architecture - Controller β Service β Repository β Entity
π Further Study
- Official Spring Boot Documentation: https://spring.io/projects/spring-boot - Comprehensive guides and reference documentation
- Spring Boot GitHub Repository: https://github.com/spring-projects/spring-boot - Source code, examples, and issue tracking
- Baeldung Spring Boot Tutorials: https://www.baeldung.com/spring-boot - In-depth tutorials on specific Spring Boot features
π Quick Reference Card
| @SpringBootApplication | Main application annotation (configuration + auto-config + component scan) |
| @RestController | REST API controller (combines @Controller + @ResponseBody) |
| @Service | Business logic layer component |
| @Repository | Data access layer component |
| @Autowired | Dependency injection (prefer constructor injection) |
| @GetMapping | Handle HTTP GET requests |
| @PostMapping | Handle HTTP POST requests |
| @PathVariable | Extract value from URI path |
| @RequestBody | Bind request body to object |
| @Entity | JPA entity class |
| @ControllerAdvice | Global exception handler |
| @Transactional | Transaction management |
| JpaRepository | Full CRUD + JPA operations interface |
| application.properties | Configuration file (or application.yml) |
| spring.profiles.active | Activate configuration profile (dev/prod/test) |