You are viewing a preview of this lesson. Sign in to start learning
Back to Java

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 SpringSpring Boot
XML configuration filesConvention over configuration
Manual bean wiringAuto-configuration
External server deploymentEmbedded server (Tomcat/Jetty)
Complex setupStarter 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:

  1. Constructor Injection (recommended):
public class UserService {
    private final UserRepository repository;
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
}
  1. Setter Injection:
public class UserService {
    private UserRepository repository;
    
    @Autowired
    public void setRepository(UserRepository repository) {
        this.repository = repository;
    }
}
  1. 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-*.

StarterPurposeKey Dependencies
spring-boot-starter-webBuild web applicationsSpring MVC, Tomcat, Jackson
spring-boot-starter-data-jpaDatabase access with JPAHibernate, Spring Data JPA
spring-boot-starter-securityAuthentication & authorizationSpring Security
spring-boot-starter-testTesting supportJUnit, Mockito, AssertJ
spring-boot-starter-actuatorProduction monitoringHealth 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 @Controller and @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:

  1. Create custom exception classes
  2. Use @ControllerAdvice for global exception handling
  3. Return appropriate HTTP status codes
  4. Provide meaningful error messages

8. Spring Boot Actuator πŸ“Š

Actuator provides production-ready features like health checks, metrics, and monitoring endpoints.

Common endpoints:

EndpointPurpose
/actuator/healthApplication health status
/actuator/infoApplication information
/actuator/metricsApplication metrics
/actuator/envEnvironment properties
/actuator/loggersLogger 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 ⚠️

  1. Using field injection instead of constructor injection

    • ❌ @Autowired private UserService service;
    • βœ… Use constructor injection for better testability and immutability
  2. Forgetting @Repository, @Service, or @Controller annotations

    • Spring won't detect your components without these stereotype annotations
    • Remember: @RestController = @Controller + @ResponseBody
  3. Not handling exceptions properly

    • Don't let exceptions bubble up as generic 500 errors
    • Always use @ControllerAdvice for global exception handling
  4. Mixing business logic in controllers

    • Controllers should be thin, delegating to service layer
    • Keep the separation: Controller β†’ Service β†’ Repository
  5. Hardcoding configuration values

    • ❌ private String dbUrl = "localhost:3306";
    • βœ… Use @Value("${spring.datasource.url}") or @ConfigurationProperties
  6. Not using Spring Boot Starters

    • Don't manually add individual dependencies
    • Use starters to get compatible, tested dependency sets
  7. Ignoring database connection pooling

    • Spring Boot configures HikariCP by default
    • Adjust pool size for production: spring.datasource.hikari.maximum-pool-size
  8. Using @Transactional incorrectly

    • Place on service methods, not controller methods
    • Understand propagation and isolation levels
  9. Exposing all Actuator endpoints in production

    • Limit exposed endpoints: management.endpoints.web.exposure.include=health,info
    • Secure actuator endpoints with Spring Security
  10. Not testing your application

    • Use @SpringBootTest for integration tests
    • Use @WebMvcTest for controller tests
    • Mock dependencies with @MockBean

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

  1. Official Spring Boot Documentation: https://spring.io/projects/spring-boot - Comprehensive guides and reference documentation
  2. Spring Boot GitHub Repository: https://github.com/spring-projects/spring-boot - Source code, examples, and issue tracking
  3. Baeldung Spring Boot Tutorials: https://www.baeldung.com/spring-boot - In-depth tutorials on specific Spring Boot features

πŸ“‹ Quick Reference Card

@SpringBootApplicationMain application annotation (configuration + auto-config + component scan)
@RestControllerREST API controller (combines @Controller + @ResponseBody)
@ServiceBusiness logic layer component
@RepositoryData access layer component
@AutowiredDependency injection (prefer constructor injection)
@GetMappingHandle HTTP GET requests
@PostMappingHandle HTTP POST requests
@PathVariableExtract value from URI path
@RequestBodyBind request body to object
@EntityJPA entity class
@ControllerAdviceGlobal exception handler
@TransactionalTransaction management
JpaRepositoryFull CRUD + JPA operations interface
application.propertiesConfiguration file (or application.yml)
spring.profiles.activeActivate configuration profile (dev/prod/test)