Spring Boot Fundamentals

Spring Boot is the dominant Java application framework. It packages Spring Framework with sensible defaults, embedded servers, and dependency starters that turn "build a web application" from a hundred lines of XML into a single annotation. Most modern Java backend code runs on Spring Boot or something close to it.

This page covers the core concepts — the parts you need to understand to use Spring Boot effectively, not the encyclopedic catalog of every annotation.

What Spring Boot adds to Spring

Spring Framework provides:

Spring Boot adds:

The trade-off: Spring Boot's "magic" auto-configuration is convenient but can be opaque when something goes wrong. Understanding what Spring Boot is doing is necessary for non-trivial debugging.

The application class

A Spring Boot application starts from a class with @SpringBootApplication:

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

@SpringBootApplication combines:

By convention, @SpringBootApplication lives at the top of the package hierarchy; component scanning finds everything underneath.

Beans

A Spring "bean" is an object managed by the IoC container. Three ways to declare:

@Component family on a class

@Component
public class OrderService { /* ... */ }

@Service     // semantic alias for @Component
@Repository  // for data access
@Controller  // for web controllers
@RestController  // controller + @ResponseBody

The container finds these via @ComponentScan. Annotated classes become beans automatically.

@Bean on methods in a @Configuration class

@Configuration
public class AppConfig {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

Useful for creating beans from third-party code (no annotations available) or for conditional bean creation.

@SpringBootApplication itself

The application class is also a @Configuration class; you can declare @Bean methods directly on it for small applications.

Dependency injection

Inject dependencies via constructor (preferred):

@Service
public class OrderService {
    private final OrderRepository repository;
    private final NotificationService notifications;

    public OrderService(OrderRepository repository, NotificationService notifications) {
        this.repository = repository;
        this.notifications = notifications;
    }
}

The container finds the matching beans and passes them in. final fields make the dependencies clearly required.

Field injection (@Autowired on a field) works but is discouraged: harder to test, hides dependencies, prevents final fields.

Auto-configuration

The signature feature. Based on what's on the classpath, Spring Boot configures things automatically:

Each starter brings a transitive dependency tree plus the auto-configuration classes that wire it up.

You can:

Configuration

External configuration via application.properties:

server.port=8080
spring.datasource.url=jdbc:postgresql://localhost/mydb
spring.datasource.username=app
logging.level.org.springframework=INFO

Inject configuration with @Value (simple) or @ConfigurationProperties (typed):

@ConfigurationProperties(prefix = "app.email")
public record EmailConfig(String host, int port, String username, String password) {}

Configuration profiles allow environment-specific settings:

application.properties
application-dev.properties
application-prod.properties

Activate a profile with --spring.profiles.active=prod.

Web controllers

REST endpoints with @RestController:

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    private final OrderService service;

    public OrderController(OrderService service) {
        this.service = service;
    }

    @GetMapping("/{id}")
    public Order get(@PathVariable String id) {
        return service.findById(id);
    }

    @PostMapping
    public Order create(@RequestBody @Valid CreateOrderRequest request) {
        return service.create(request);
    }
}

Methods become endpoints; parameters are bound from path, query, headers, or body.

Data access

@Repository interfaces extending JpaRepository:

public interface OrderRepository extends JpaRepository<Order, String> {
    List<Order> findByStatus(OrderStatus status);
}

Spring Data implements the interface at runtime. Method names are parsed into queries (findByStatusWHERE status = ?).

For complex queries, @Query annotations or QueryDSL.

Testing

@SpringBootTest loads the full application context:

@SpringBootTest
class OrderServiceTest {
    @Autowired
    OrderService service;

    @Test
    void shouldCreateOrder() {
        Order order = service.create(...);
        assertThat(order).isNotNull();
    }
}

For faster tests, slices like @WebMvcTest (controller layer only) or @DataJpaTest (data layer only) load a smaller context.

@MockBean replaces a real bean with a Mockito mock for tests.

What to learn next

The fundamentals above cover ~80% of typical Spring Boot usage. Beyond that:

Common failure patterns

Further Reading