Skip to content

Spring Boot Basics — Complete Guide

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Spring Boot Basics. We cover key concepts, practical examples, and best practices to help you master this topic.

Why Spring Boot?

Spring revolutionized Java enterprise development by introducing dependency injection and aspect-oriented programming, replacing the heavyweight EJB model. However, traditional Spring required extensive XML configuration, dependency management conflicts, and complex setup. Spring Boot emerged as the solution: it embeds Tomcat, auto-configures Spring based on classpath dependencies, provides production-ready metrics and health checks, and eliminates boilerplate configuration.

Spring Boot's philosophy is "opinionated defaults." It makes reasonable assumptions about your infrastructure and configures them automatically. If you have H2 on the classpath, you get an in-memory database. If you have spring-web on the classpath, you get an embedded Tomcat server. You only write configuration when you need to override the defaults.

flowchart TB
    subgraph SB[Spring Boot Application]
        AC[Auto-Configuration] --> Container[Embedded Tomcat/Jetty]
        DI[Dependency Injection] --> Beans
        Beans --> Controller[Web Controllers]
        Beans --> Service[Service Layer]
        Beans --> Repository[Data Repositories]
        AC --> Actuator[Actuator - Metrics]
        AC --> Config[Externalized Config]
    end
    Controller --> Service
    Service --> Repository
    Repository --> DB[(Database)]
    Client[HTTP Client] --> Controller
    Actuator --> Metrics[Metrics Endpoints]

Setting Up a Spring Boot Project

Using Spring Initializr

The quickest way to create a Spring Boot project is via start.spring.io or your IDE.

Maven Dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.0</version>
</parent>

<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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

The Main Application Class

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@SpringBootApplication combines three annotations: @Configuration, @EnableAutoConfiguration, and @ComponentScan. When you run the main method, Spring Boot starts an embedded Tomcat server, scans for components, and auto-configures beans based on classpath dependencies.

Building a REST Controller

package com.example.demo.controller;

import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.*;

@RestController
@RequestMapping("/api/hello")
public class HelloController {
    
    @GetMapping
    public Map<String, String> greet(@RequestParam(defaultValue = "World") String name) {
        return Map.of(
            "message", "Hello, " + name + "!",
            "timestamp", LocalDateTime.now().toString()
        );
    }
    
    @GetMapping("/{id}")
    public String greetById(@PathVariable int id) {
        return "Hello, user " + id;
    }
}

Testing the Controller

curl http://localhost:8080/api/hello?name=Alice

Output:

{"message":"Hello, Alice!","timestamp":"2026-06-28T10:30:00"}

Dependency Injection in Spring

Spring manages object creation and wiring through its IoC container. Components declare their dependencies, and Spring injects them.

// Service interface
public interface GreetingService {
    String generateGreeting(String name);
}

// Service implementation
@Service
public class FormalGreetingService implements GreetingService {
    @Override
    public String generateGreeting(String name) {
        return "Good day, " + name + ".";
    }
}

// Controller with dependency injection
@RestController
@RequestMapping("/api/greet")
public class GreetingController {
    
    private final GreetingService greetingService;
    
    // Constructor injection (preferred)
    public GreetingController(GreetingService greetingService) {
        this.greetingService = greetingService;
    }
    
    @GetMapping
    public String greet(@RequestParam String name) {
        return greetingService.generateGreeting(name);
    }
}

Injection Types

Type Description When to Use
Constructor Final fields, explicit dependencies Always preferred
Setter Optional dependencies For optional config
Field Direct annotation on field Discouraged (hard to test)

Configuration and Properties

application.yml

server:
  port: 9090
  servlet:
    context-path: /api

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: ${DB_USERNAME:appuser}
    password: ${DB_PASSWORD:secret}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true

app:
  feature-flags:
    new-checkout: true
    beta-program: false

Type-Safe Configuration Properties

@ConfigurationProperties(prefix = "app.feature-flags")
@Component
public class FeatureFlags {
    private boolean newCheckout;
    private boolean betaProgram;
    
    // getters and setters
}

@Service
public class CheckoutService {
    private final FeatureFlags featureFlags;
    
    public CheckoutService(FeatureFlags featureFlags) {
        this.featureFlags = featureFlags;
    }
    
    public void processCheckout(Order order) {
        if (featureFlags.isNewCheckout()) {
            // Use new checkout flow
        } else {
            // Use legacy checkout flow
        }
    }
}

Profiles

Profiles allow different configurations for different environments.

# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb
  jpa:
    hibernate:
      ddl-auto: create-drop
logging:
  level:
    com.example: DEBUG
# Run with dev profile
java -jar myapp.jar --spring.profiles.active=dev

# Or via environment variable
export SPRING_PROFILES_ACTIVE=dev,cloud

Spring Boot Actuator

Actuator provides production-ready endpoints for monitoring and management.

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env,loggers
  endpoint:
    health:
      show-details: always
# Health check
curl http://localhost:8080/actuator/health

# Application info
curl http://localhost:8080/actuator/info

# Metrics
curl http://localhost:8080/actuator/metrics/jvm.memory.used

Common Mistakes

1. Scanning Too Many Packages

If @SpringBootApplication is placed in a package that does not cover your components, they will not be scanned.

// Main class at com.example.app
// Components at com.example.app.controller - works
// Components at com.example.legacy.controller - NOT scanned

Specify @SpringBootApplication(scanBasePackages = "com.example") if needed.

2. Circular Dependencies

Bean A depends on Bean B, and Bean B depends on Bean A. Spring can sometimes resolve this with lazy initialization, but it is a design smell.

3. Field Injection for Testing

// Hard to test: cannot easily mock greetingService
@RestController
public class TestController {
    @Autowired
    private GreetingService greetingService;
}

// Easy to test: inject mock in constructor
@RestController
public class TestController {
    private final GreetingService greetingService;
    
    public TestController(GreetingService greetingService) {
        this.greetingService = greetingService;
    }
}

4. Not Using the Right Starter

Spring Boot starters group dependencies logically. Using spring-boot-starter-web for a non-web application brings unnecessary dependencies.

5. Ignoring the Failure Analyzer

Spring Boot's failure analyzer provides actionable error messages. Read the full stack trace and analysis output when an application fails to start.

6. Exposing Actuator Endpoints in Production

# Restrict actuator access in production
management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    shutdown:
      enabled: false

Practice Questions

  1. What does @SpringBootApplication encapsulate? What happens if you omit it and use each annotation individually?
  2. How does Spring Boot's auto-configuration decide which beans to configure?
  3. What is the difference between @Component, @Service, @Repository, and @Controller?
  4. How do you externalize configuration in a Spring Boot application?
  5. What is the purpose of spring-boot-starter-parent?

Challenge: Create a Spring Boot application that exposes a REST API for a task management system. Implement CRUD operations for tasks (id, title, description, status, dueDate), use an H2 in-memory database, and include validation. Add a custom health indicator that checks whether all database tables exist.

FAQ

What is the difference between Spring and Spring Boot?

Spring is a framework for dependency injection and enterprise features. Spring Boot is an extension that auto-configures Spring based on classpath, embeds servers, and provides production-ready features out of the box.

Can I deploy Spring Boot applications to external Tomcat?

Yes. Change the packaging to WAR in pom.xml, extend SpringBootServletInitializer, and deploy to your external Tomcat instance. However, the embedded server approach is simpler and more common.

How does Spring Boot handle database migrations?

Spring Boot integrates with Flyway and Liquibase auto-configuration. Add the dependency, place migration scripts in db/migration/, and Spring Boot runs them on startup.

What is the difference between @RestController and @Controller?

@RestController combines @Controller and @ResponseBody, meaning every method returns the response body directly (typically JSON). @Controller is used for view resolution (JSP, Thymeleaf).

How do I configure CORS in Spring Boot?

Use @CrossOrigin on controllers or methods, or define a WebMvcConfigurer bean that adds CORS mappings globally. Spring Boot reads the cors.allowed-origins property for basic setup.

Mini Project: RESTful Blog API

Build a RESTful blog API with Spring Boot that provides:

  • User registration and authentication with JWT tokens
  • CRUD operations for blog posts with pagination and sorting
  • Category and tag management for posts
  • Comment system on posts
  • Search posts by title or content
  • Swagger/OpenAPI documentation via springdoc-openapi
  • Integration tests with @WebMvcTest and @DataJpaTest

Use Spring Data JPA for persistence, validation with Jakarta Bean Validation, and global Exception Handling with @ControllerAdvice.

What's Next

You have built a solid foundation with Spring Boot. In the next lesson, we will dive deeper into data access with Spring Data JPA, learning about repositories, entity relationships, and query methods.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro