Spring BootSpring Boot Starters
Spring Boot

Spring Boot Starters

Spring Boot starters are curated dependency descriptors that bring in everything needed for a specific feature — all at compatible, tested versions. Instead of manually hunting down and version-matching dozens of libraries, you add one starter and get a complete, working setup with auto-configuration included.

What Is a Starter?

A Spring Boot starter is a special Maven or Gradle dependency that doesn't contain code itself — it's a descriptor that pulls in a curated set of related dependencies at tested, compatible versions. The starter's pom.xml declares all the transitive dependencies you need for a feature, and Spring Boot's parent POM manages their versions. Before starters, adding JPA support to a Spring application meant manually finding and adding: spring-orm, spring-jdbc, hibernate-core, hibernate-entitymanager, the JDBC driver, a connection pool library, and spring-data-jpa — all at compatible versions. One wrong version caused cryptic runtime failures that could take hours to debug. With starters, you add one line: spring-boot-starter-data-jpa. All seven libraries are included at versions guaranteed to work together, and Spring Boot auto-configures everything automatically. Starter naming convention: - Official Spring Boot starters: spring-boot-starter-{name} - Third-party starters: {name}-spring-boot-starter

How Starters Work Under the Hood

A starter is just a POM file with dependencies — no Java code. When Maven or Gradle resolves it, all transitive dependencies download automatically. Spring Boot's parent POM (spring-boot-dependencies BOM) defines the exact version of every library in the ecosystem, eliminating version conflicts entirely.
XML
<!-- Your pom.xml — inherit Spring Boot's dependency management: -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
</parent>

<dependencies>
    <!-- One line — replaces 20+ manually-managed dependencies: -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!-- No version needed — inherited from parent BOM -->
    </dependency>
</dependencies>

<!-- What spring-boot-starter-web actually pulls in (its internal pom.xml): -->
<!--
    spring-boot-starter               (core Spring Boot + logging)
    spring-boot-starter-json          (Jackson for JSON serialization)
    spring-boot-starter-tomcat        (embedded Tomcat server)
    spring-web                        (Spring Web core)
    spring-webmvc                     (Spring MVC framework)
    jakarta.annotation-api            (Jakarta annotations)
-->
<!-- All at tested, compatible versions — zero version conflicts -->

<!-- Gradle equivalent: -->
<!-- plugins { id 'org.springframework.boot' version '3.2.0' } -->
<!-- implementation 'org.springframework.boot:spring-boot-starter-web' -->

The Foundation Starter

spring-boot-starter is the base that every other official starter depends on. It provides the core Spring Boot infrastructure, Spring Framework core, logging via SLF4J and Logback, and YAML support. You rarely add it directly — it comes in transitively.
XML
<!-- Base starter — included automatically by all other starters: -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

// What it provides:
// - spring-boot               (SpringApplication, auto-configuration support)
// - spring-boot-autoconfigure (all auto-configuration classes)
// - spring-boot-starter-logging (SLF4J + Logback)
// - spring-core, spring-context (IoC container, DI)
// - snakeyaml               (application.yml support)
// - jakarta.annotation-api  (@PostConstruct, @PreDestroy)

// The result — out of the box with JUST spring-boot-starter:
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
// You get: IoC container, DI, @Service/@Component/@Repository scanning,
// auto-configuration engine, application.properties/yml loading,
// profiles, externalized config, SLF4J logging — all working

Web Starters

Starters for building web applications and APIs — the most commonly used category:
XML
<!-- spring-boot-starter-web — REST APIs and traditional MVC web apps -->
<!-- Provides: Spring MVC, embedded Tomcat, Jackson JSON, Bean Validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

// Enables:
@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        return ResponseEntity.ok(productService.findById(id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Product createProduct(@RequestBody @Valid CreateProductRequest request) {
        return productService.create(request);
    }
}

<!-- spring-boot-starter-webflux — reactive, non-blocking web apps -->
<!-- Provides: Spring WebFlux, embedded Netty, Reactor, Jackson -->
<!-- Use for: high-concurrency APIs, streaming, reactive pipelines -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

// Reactive controller:
@RestController
public class ReactiveController {

    @GetMapping("/stream")
    public Flux<Product> streamProducts() {
        return productService.findAll()
            .delayElements(Duration.ofMillis(100));  // stream with delay
    }

    @GetMapping("/{id}")
    public Mono<Product> getProduct(@PathVariable Long id) {
        return productService.findById(id)
            .switchIfEmpty(Mono.error(new ProductNotFoundException(id)));
    }
}

<!-- spring-boot-starter-thymeleaf — server-side HTML rendering -->
<!-- Provides: Thymeleaf template engine, Spring + Thymeleaf integration -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- spring-boot-starter-websocket — WebSocket support -->
<!-- Provides: Spring WebSocket, STOMP messaging -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

Data Access Starters

Starters for every major data storage technology — relational databases, NoSQL, caching, and search:
XML
<!-- spring-boot-starter-data-jpa — relational DB with JPA and Hibernate -->
<!-- Provides: Spring Data JPA, Hibernate ORM, HikariCP, Spring JDBC -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Always pair with a JDBC driver: -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Or PostgreSQL: -->
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

// application.properties for JPA: spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=secret spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true

// Repository — Spring Data generates the implementation:
public interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByCustomerIdAndStatus(Long customerId, OrderStatus status);
    Page<Order> findByCreatedAtAfter(LocalDateTime date, Pageable pageable);

    @Query("SELECT o FROM Order o WHERE o.total > :amount")
    List<Order> findHighValueOrders(@Param("amount") BigDecimal amount);
}

<!-- spring-boot-starter-jdbc — JDBC without full JPA overhead -->
<!-- Provides: Spring JDBC, JdbcTemplate, NamedParameterJdbcTemplate, HikariCP -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

// JdbcTemplate usage:
@Repository
public class ProductRepository {
    private final JdbcTemplate jdbc;

    public ProductRepository(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    public List<Product> findAll() {
        return jdbc.query(
            "SELECT id, name, price FROM products",
            (rs, row) -> new Product(rs.getLong("id"), rs.getString("name"),
                                     rs.getBigDecimal("price"))
        );
    }
}

<!-- spring-boot-starter-data-mongodb — MongoDB document database -->
<!-- Provides: Spring Data MongoDB, MongoDB Java driver -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

// application.properties: spring.data.mongodb.uri=mongodb://localhost:27017/mydb

// MongoDB repository:
@Document(collection = "products")
public class Product {
    @Id private String id;
    private String name;
    private BigDecimal price;
    private List<String> tags;
}

public interface ProductRepository extends MongoRepository<Product, String> {
    List<Product> findByTagsContaining(String tag);
    List<Product> findByPriceLessThan(BigDecimal maxPrice);
}

<!-- spring-boot-starter-data-redis — Redis for caching and sessions -->
<!-- Provides: Spring Data Redis, Lettuce Redis client -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

// application.properties: spring.data.redis.host=localhost spring.data.redis.port=6379

// Redis caching:
@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product findById(Long id) {
        return repository.findById(id).orElseThrow();
    }

    @CacheEvict(value = "products", key = "#product.id")
    public Product update(Product product) {
        return repository.save(product);
    }
}

<!-- spring-boot-starter-data-elasticsearch — full-text search -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

<!-- H2 in-memory database — for development and tests: -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
// Access H2 console at http://localhost:8080/h2-console (with DevTools)

Security Starters

Starters for securing Spring Boot applications — from basic authentication to OAuth2 and JWT:
XML
<!-- spring-boot-starter-security — authentication and authorization -->
<!-- Provides: Spring Security, crypto, CSRF protection, session management -->
<!-- Auto-configures: HTTP Basic, form login, default user with random password -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

// Minimal security configuration:
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**", "/actuator/health").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
            .build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

<!-- spring-boot-starter-oauth2-resource-server — validate JWT tokens -->
<!-- Provides: Spring Security OAuth2 Resource Server, nimbus-jose-jwt -->
<!-- Use when: your API validates Bearer tokens issued by an auth server -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

// application.properties: spring.security.oauth2.resourceserver.jwt.issuer-uri=https://auth.example.com

// Security config for JWT resource server:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
        )
        .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
        .build();
}

<!-- spring-boot-starter-oauth2-client — login with Google, GitHub, etc. -->
<!-- Provides: Spring Security OAuth2 Client, OpenID Connect support -->
<!-- Use when: your app delegates authentication to an external provider -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

// application.properties: spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID} spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET}
// Adds /oauth2/authorization/google login endpoint automatically

Messaging Starters

Starters for asynchronous messaging and event-driven architectures:
XML
<!-- spring-boot-starter-amqp — RabbitMQ messaging -->
<!-- Provides: Spring AMQP, RabbitTemplate, RabbitMQ Java client -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

// application.properties: spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest

// Produce a message:
@Service
public class OrderEventPublisher {
    private final RabbitTemplate rabbitTemplate;

    public void publishOrderCreated(Order order) {
        rabbitTemplate.convertAndSend("orders.exchange", "order.created", order);
    }
}

// Consume a message:
@Component
public class OrderEventConsumer {

    @RabbitListener(queues = "order.created.queue")
    public void handleOrderCreated(Order order) {
        log.info("Processing order: {}", order.getId());
        inventoryService.reserve(order);
    }
}

<!-- spring-kafka — Apache Kafka (not a Boot starter, direct Spring Kafka dep) -->
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

// application.properties: spring.kafka.bootstrap-servers=localhost:9092 spring.kafka.consumer.group-id=my-app spring.kafka.consumer.auto-offset-reset=earliest

// Kafka producer:
@Service
public class EventPublisher {
    private final KafkaTemplate<String, Object> kafkaTemplate;

    public void publish(String topic, Object event) {
        kafkaTemplate.send(topic, event);
    }
}

// Kafka consumer:
@Component
public class EventConsumer {

    @KafkaListener(topics = "user-events", groupId = "my-app")
    public void consume(UserEvent event) {
        log.info("Received event: {}", event.getType());
    }
}

<!-- spring-boot-starter-mail — send emails -->
<!-- Provides: Spring Mail, JavaMailSender, Jakarta Mail -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

// application.properties: spring.mail.host=smtp.gmail.com spring.mail.port=587 spring.mail.username=${MAIL_USERNAME} spring.mail.password=${MAIL_PASSWORD} spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true

// Send email:
@Service
public class EmailService {
    private final JavaMailSender mailSender;

    public void sendWelcome(String to, String name) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(to);
        message.setSubject("Welcome to MyApp!");
        message.setText("Hi " + name + ", welcome aboard!");
        mailSender.send(message);
    }
}

Validation, Actuator, and Testing Starters

Essential starters for input validation, production monitoring, and comprehensive testing:
XML
<!-- spring-boot-starter-validation — Bean Validation (Jakarta Validation API) -->
<!-- Provides: Hibernate Validator, validation annotations -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

// Annotate request DTOs:
public record CreateUserRequest(
    @NotBlank(message = "Email is required")
    @Email(message = "Must be a valid email address")
    String email,

    @NotBlank(message = "Name is required")
    @Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
    String name,

    @NotBlank(message = "Password is required")
    @Size(min = 8, message = "Password must be at least 8 characters")
    String password,

    @Min(value = 0, message = "Age cannot be negative")
    @Max(value = 150, message = "Age is not realistic")
    Integer age
) { }

// Trigger validation with @Valid in the controller:
@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED)
public UserResponse createUser(@RequestBody @Valid CreateUserRequest request) {
    return userService.createUser(request);
}
// Returns HTTP 400 with field-level error details automatically if validation fails

// Validate method parameters (add @Validated on the class):
@Service
@Validated
public class UserService {
    public User findByEmail(@NotBlank @Email String email) {
        return userRepository.findByEmail(email).orElseThrow();
    }
}

<!-- spring-boot-starter-actuator — production monitoring and management -->
<!-- Provides: health checks, metrics, env, beans, mappings endpoints -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

// application.properties: management.endpoints.web.exposure.include=health,info,metrics,loggers management.endpoint.health.show-details=when-authorized management.info.env.enabled=true info.app.name=My Application info.app.version=2.1.0

<!-- spring-boot-starter-test — complete testing toolkit -->
<!-- Provides: JUnit 5, Mockito, AssertJ, MockMVC, Spring Test, Hamcrest -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

// Unit test — no Spring context needed:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @Mock
    private EmailService emailService;

    @InjectMocks
    private UserService userService;

    @Test
    void createUser_withDuplicateEmail_throwsException() {
        when(userRepository.existsByEmail("alice@test.com")).thenReturn(true);

        assertThatThrownBy(() -> userService.createUser(
            new CreateUserRequest("alice@test.com", "Alice", "pass123", 25)))
            .isInstanceOf(DuplicateEmailException.class);

        verify(userRepository, never()).save(any());
    }
}

// Controller test — MVC layer only:
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired private MockMvc mockMvc;
    @MockBean private UserService userService;
    @Autowired private ObjectMapper objectMapper;

    @Test
    void createUser_withValidRequest_returnsCreated() throws Exception {
        CreateUserRequest req = new CreateUserRequest("alice@test.com", "Alice", "pass1234", 25);
        UserResponse res = new UserResponse(1L, "alice@test.com", "Alice");

        when(userService.createUser(any())).thenReturn(res);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(req)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").value(1))
            .andExpect(jsonPath("$.email").value("alice@test.com"));
    }
}

Customizing Starters — Excluding Dependencies

Sometimes you want a starter but not all of its included libraries. Exclude specific transitive dependencies and substitute your own.
XML
<!-- Replace embedded Tomcat with Jetty: -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

<!-- Replace Logback with Log4j2 (better async performance): -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

<!-- Replace HikariCP connection pool with DBCP2: -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
</dependency>

// Gradle exclusions:
// implementation('org.springframework.boot:spring-boot-starter-web') {
//     exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
// }
// implementation 'org.springframework.boot:spring-boot-starter-jetty'

Creating a Custom Starter

Teams with shared infrastructure code can package it as a custom starter — a reusable module that auto-configures itself when added as a dependency to any Spring Boot application. This is exactly how third-party libraries like AWS SDK, Micrometer, and OpenTelemetry integrate with Spring Boot.
Java
// Custom starter project structure:
// audit-spring-boot-starter/
//   src/main/java/com/example/audit/
//     AuditAutoConfiguration.java
//     AuditProperties.java
//     AuditService.java
//     AuditRepository.java
//   src/main/resources/META-INF/spring/
//     org.springframework.boot.autoconfigure.AutoConfiguration.imports

// Step 1 — Define the properties class:
@ConfigurationProperties(prefix = "audit")
public class AuditProperties {
    private boolean enabled = true;
    private int retentionDays = 90;
    private String tableName = "audit_log";
    // getters and setters
}

// Step 2 — Define the auto-configuration class:
@AutoConfiguration
@ConditionalOnClass(AuditService.class)
@ConditionalOnProperty(prefix = "audit", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(AuditProperties.class)
public class AuditAutoConfiguration {

    // @ConditionalOnMissingBean — backs off if app defines its own AuditService:
    @Bean
    @ConditionalOnMissingBean
    public AuditService auditService(AuditProperties properties,
                                     JdbcTemplate jdbcTemplate) {
        return new AuditService(jdbcTemplate, properties.getTableName(),
                                properties.getRetentionDays());
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(AuditService.class)
    public AuditAspect auditAspect(AuditService auditService) {
        return new AuditAspect(auditService);
    }
}

// Step 3 — Register the auto-configuration:
// File: src/main/resources/META-INF/spring/
//        org.springframework.boot.autoconfigure.AutoConfiguration.imports
// Contents (one class per line):
// com.example.audit.AuditAutoConfiguration

// Step 4 — pom.xml for the starter:
<dependencies>
    <!-- Core Spring Boot — don't include web or JPA unless required: -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <!-- Optional: annotation processor for IDE auto-completion: -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

// Using the custom starter in any project:
<dependency>
    <groupId>com.example</groupId>
    <artifactId>audit-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

// application.properties in the consuming project:
audit.enabled=true
audit.retention-days=30

// AuditService is now injectable in any bean — zero config required:
@Service
public class OrderService {
    private final AuditService auditService;  // auto-configured by the starter

    public Order createOrder(OrderRequest request) {
        Order order = processOrder(request);
        auditService.log("ORDER_CREATED", order.getId(), getCurrentUser());
        return order;
    }
}

Complete Starter Reference

A comprehensive reference of all official Spring Boot starters organized by category:
Java
// ── WEB ──────────────────────────────────────────────────────────
// spring-boot-starter-web           REST APIs, Spring MVC, embedded Tomcat
// spring-boot-starter-webflux       Reactive web, Spring WebFlux, Netty
// spring-boot-starter-websocket     WebSocket + STOMP messaging
// spring-boot-starter-thymeleaf     Thymeleaf server-side templates
// spring-boot-starter-freemarker    FreeMarker templates
// spring-boot-starter-mustache      Mustache templates
// spring-boot-starter-graphql       Spring for GraphQL

// ── DATA ACCESS ───────────────────────────────────────────────────
// spring-boot-starter-data-jpa      JPA + Hibernate + Spring Data JPA
// spring-boot-starter-jdbc          JdbcTemplate, no JPA overhead
// spring-boot-starter-data-mongodb  Spring Data MongoDB
// spring-boot-starter-data-redis    Spring Data Redis + Lettuce
// spring-boot-starter-data-r2dbc    Reactive relational DB access (R2DBC)
// spring-boot-starter-data-elasticsearch  Elasticsearch
// spring-boot-starter-data-cassandra  Apache Cassandra
// spring-boot-starter-data-neo4j    Neo4j graph database
// spring-boot-starter-data-ldap     LDAP directory access

// ── SECURITY ──────────────────────────────────────────────────────
// spring-boot-starter-security      Spring Security (auth + authz)
// spring-boot-starter-oauth2-resource-server  JWT token validation
// spring-boot-starter-oauth2-client Social login (Google, GitHub, etc.)
// spring-boot-starter-oauth2-authorization-server  OAuth2 auth server

// ── MESSAGING ─────────────────────────────────────────────────────
// spring-boot-starter-amqp          RabbitMQ via Spring AMQP
// spring-boot-starter-activemq      Apache ActiveMQ
// spring-boot-starter-artemis       Apache ActiveMQ Artemis
// spring-boot-starter-mail          JavaMailSender, SMTP email
// spring-kafka                      Apache Kafka (direct, not a Boot starter)

// ── OPERATIONS ────────────────────────────────────────────────────
// spring-boot-starter-actuator      Health, metrics, env, beans endpoints
// spring-boot-starter-validation    Bean Validation (Jakarta Validation API)
// spring-boot-starter-aop           AspectJ AOP support
// spring-boot-starter-cache         Spring Cache abstraction (@Cacheable)
// spring-boot-starter-quartz        Quartz job scheduler

// ── TESTING ───────────────────────────────────────────────────────
// spring-boot-starter-test          JUnit 5 + Mockito + AssertJ + MockMVC

// ── SERVERS (use as replacement for default Tomcat) ───────────────
// spring-boot-starter-tomcat        Embedded Tomcat (default)
// spring-boot-starter-jetty         Embedded Jetty
// spring-boot-starter-undertow      Embedded Undertow (fastest)

// ── LOGGING (use as replacement for default Logback) ─────────────
// spring-boot-starter-logging       SLF4J + Logback (default)
// spring-boot-starter-log4j2        SLF4J + Log4j2 (better async)