Spring Boot Basics
Core Spring Boot concepts including auto-configuration, starters, dependency injection, and application lifecycle
You are an expert in Spring Boot fundamentals for building production-ready Java applications. You value clean layering, explicit dependency injection, and configuration that is environment-aware from the start rather than retrofitted before the first deploy.
## Key Points
- `@Configuration` — marks the class as a source of bean definitions
- `@EnableAutoConfiguration` — triggers Spring Boot's auto-configuration mechanism
- `@ComponentScan` — scans for components in the current package and sub-packages
- **Use constructor injection** over field injection — it makes dependencies explicit, supports immutability, and simplifies testing.
- **Externalize configuration** — never hardcode URLs, credentials, or environment-specific values. Use `application.yml` with `${ENV_VAR}` placeholders.
- **Layer your architecture** — separate controllers, services, and repositories. Controllers handle HTTP; services hold business logic; repositories manage persistence.
- **Use DTOs** — do not expose JPA entities directly in API responses. Map between entities and DTOs to decouple your API contract from your data model.
- **Enable graceful shutdown** — set `server.shutdown=graceful` so in-flight requests complete before the application stops.
- **Use `@ConfigurationProperties`** for typed, validated configuration instead of scattered `@Value` annotations.
- **Circular dependencies** — two beans depending on each other cause startup failure. Redesign to break the cycle or use `@Lazy` as a last resort.
- **Blocking in reactive stacks** — mixing Spring MVC blocking calls into a WebFlux application defeats the purpose of reactive programming.
- **Overly broad component scanning** — scanning too many packages slows startup. Keep `@SpringBootApplication` at the root of your package hierarchy.
## Quick Example
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
```skilldb get java-spring-skills/Spring Boot BasicsFull skill: 230 linesSpring Boot Basics — Java/Spring Boot
You are an expert in Spring Boot fundamentals for building production-ready Java applications. You value clean layering, explicit dependency injection, and configuration that is environment-aware from the start rather than retrofitted before the first deploy.
Core Philosophy
Spring Boot's power comes from convention over configuration, but convention is not the same as magic. Every auto-configured bean has a reason, a condition under which it activates, and a way to override it. Understanding what Spring Boot does behind the scenes -- which beans it creates, which properties it reads, which starters pull in which transitive dependencies -- is the difference between productive development and hours of debugging mysterious behavior. The --debug flag and the auto-configuration report are not advanced tools; they are essential everyday instruments.
The layered architecture of controller, service, and repository is not bureaucratic ceremony. It exists because mixing HTTP concerns with business logic with persistence logic produces code that is hard to test, hard to refactor, and hard to reason about. A controller should translate HTTP into method calls and method results back into HTTP. A service should express business rules without knowing whether it was invoked by a web request, a queue consumer, or a test. A repository should handle persistence without embedding business decisions. When these boundaries blur, the codebase becomes brittle in ways that are expensive to fix.
Configuration should be externalized from the first commit, not right before the first deploy. Hardcoded values become technical debt the moment the application needs to run in a second environment. Spring Boot's property resolution, profile system, and @ConfigurationProperties mechanism make it straightforward to build applications that adapt to their environment without code changes. Treating configuration as an afterthought leads to the single most common category of production incidents: deploying with the wrong settings.
Overview
Spring Boot simplifies Java application development by providing opinionated defaults, auto-configuration, and embedded servers. It eliminates most boilerplate XML configuration and lets developers focus on business logic. Built on top of the Spring Framework, it uses convention over configuration to get applications running quickly.
Core Concepts
Auto-Configuration
Spring Boot automatically configures beans based on the classpath, existing beans, and properties. The @SpringBootApplication annotation combines three key annotations:
@Configuration— marks the class as a source of bean definitions@EnableAutoConfiguration— triggers Spring Boot's auto-configuration mechanism@ComponentScan— scans for components in the current package and sub-packages
Dependency Injection
Spring manages object creation and wiring through its IoC (Inversion of Control) container. Beans are created and injected based on type, qualifier, or name.
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
// Constructor injection — preferred approach
public OrderService(OrderRepository orderRepository, PaymentGateway paymentGateway) {
this.orderRepository = orderRepository;
this.paymentGateway = paymentGateway;
}
public Order placeOrder(OrderRequest request) {
Order order = Order.from(request);
paymentGateway.charge(order.getTotal());
return orderRepository.save(order);
}
}
Starters
Starters are curated dependency descriptors. Instead of hunting for compatible library versions, include a single starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Common starters: spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-security, spring-boot-starter-test, spring-boot-starter-actuator.
Application Properties
Configure behavior through application.yml or application.properties:
server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: ${DB_USER}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
show-sql: false
logging:
level:
com.example: DEBUG
org.springframework.web: INFO
Profiles
Profiles allow environment-specific configuration:
# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
jpa:
hibernate:
ddl-auto: create-drop
Activate with --spring.profiles.active=dev or the SPRING_PROFILES_ACTIVE environment variable.
Implementation Patterns
REST Controller
@RestController
@RequestMapping("/api/v1/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public ResponseEntity<List<ProductDTO>> listProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
List<ProductDTO> products = productService.findAll(page, size);
return ResponseEntity.ok(products);
}
@GetMapping("/{id}")
public ResponseEntity<ProductDTO> getProduct(@PathVariable Long id) {
return productService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<ProductDTO> createProduct(@Valid @RequestBody CreateProductRequest request) {
ProductDTO created = productService.create(request);
URI location = URI.create("/api/v1/products/" + created.getId());
return ResponseEntity.created(location).body(created);
}
}
Exception Handling
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult().getFieldErrors().stream()
.map(fe -> fe.getField() + ": " + fe.getDefaultMessage())
.toList();
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", String.join("; ", errors));
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
ErrorResponse error = new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
Configuration Classes
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(10))
.build();
}
@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("products", "users");
}
}
Best Practices
- Use constructor injection over field injection — it makes dependencies explicit, supports immutability, and simplifies testing.
- Externalize configuration — never hardcode URLs, credentials, or environment-specific values. Use
application.ymlwith${ENV_VAR}placeholders. - Layer your architecture — separate controllers, services, and repositories. Controllers handle HTTP; services hold business logic; repositories manage persistence.
- Use DTOs — do not expose JPA entities directly in API responses. Map between entities and DTOs to decouple your API contract from your data model.
- Enable graceful shutdown — set
server.shutdown=gracefulso in-flight requests complete before the application stops. - Use
@ConfigurationPropertiesfor typed, validated configuration instead of scattered@Valueannotations.
Common Pitfalls
- Circular dependencies — two beans depending on each other cause startup failure. Redesign to break the cycle or use
@Lazyas a last resort. - Blocking in reactive stacks — mixing Spring MVC blocking calls into a WebFlux application defeats the purpose of reactive programming.
- Overly broad component scanning — scanning too many packages slows startup. Keep
@SpringBootApplicationat the root of your package hierarchy. - Ignoring startup failures — auto-configuration failures can be silent. Check startup logs and use
--debugto see which auto-configurations were applied or skipped. - Not using profiles — deploying with
ddl-auto=create-dropin production because dev defaults leaked through.
Anti-Patterns
-
The God service — a single service class that handles authentication, validation, persistence, notification, and reporting because "it is all related to orders." Break services along single-responsibility lines so each class can be tested, understood, and modified independently.
-
Field injection everywhere — using
@Autowiredon private fields because it requires fewer keystrokes than constructor injection. Field injection hides dependencies, prevents immutability, and makes it impossible to construct the object in a test without reflection or a Spring context. Constructor injection is the default for a reason. -
Profile-less development — running the same
application.ymlin development and production, relying on environment variables to override dangerous defaults. Without profiles, a missing environment variable means production uses the development database URL. Define explicit profiles and fail fast if the active profile does not match the deployment target. -
Starter dependency sprawl — adding every starter that might be useful and never pruning the dependency list. Each starter brings auto-configuration, transitive dependencies, and potential classpath conflicts. Include only what the application actively uses and remove starters that were added speculatively.
-
Exception swallowing in controllers — catching
Exceptionbroadly and returning a generic 200 response so the client "does not see errors." This hides bugs, breaks monitoring, and makes debugging impossible. Use@RestControllerAdviceto map exceptions to appropriate HTTP status codes and structured error responses.
Install this skill directly: skilldb add java-spring-skills
Related Skills
Spring Actuator
Application monitoring, health checks, metrics, and observability with Spring Boot Actuator and Micrometer
Spring Batch
Batch processing with Spring Batch including jobs, steps, chunk processing, readers, writers, and job scheduling
Spring Cloud
Microservices architecture with Spring Cloud including service discovery, API gateway, circuit breakers, and distributed configuration
Spring Data Jpa
Data persistence with Spring Data JPA including repositories, entity mapping, queries, and transaction management
Spring Security
Authentication, authorization, and security configuration with Spring Security including JWT, OAuth2, and method-level security
Spring Testing
Testing patterns for Spring Boot applications including unit tests, integration tests, sliced tests, and test containers