Introduction
Auto-Configuration is the "magic" that makes Spring Boot different from pure Spring Framework. Understanding how it works will help you debug effectively and customize the application as desired. In this article, we will explore IoC Containers, the Auto-Configuration mechanism, and how to manage application configuration.
1. Inversion of Control (IoC) Containers
1.1 What is IoC?
In traditional programming, your code creates and manages objects:
// Truyền thống: bạn tạo dependencies
public class OrderService {
private final OrderRepository repository = new OrderRepository();
private final EmailService emailService = new EmailService();
}
With IoC, the framework manages the creation of objects:
// IoC: framework inject dependencies
@Service
public class OrderService {
private final OrderRepository repository;
private final EmailService emailService;
// Spring tự động inject các dependencies
public OrderService(OrderRepository repository, EmailService emailService) {
this.repository = repository;
this.emailService = emailService;
}
}
1.2 ApplicationContext vs BeanFactory
Spring IoC Container has 2 main interfaces:
BeanFactory (interface)
└── ApplicationContext (interface, extends BeanFactory)
├── AnnotationConfigApplicationContext
├── GenericWebApplicationContext
└── ...
| Features | BeanFactory | ApplicationContext |
|---|---|---|
| Bean instantiation | Lazy | Eager (default) |
| Event publishing | No | Yes |
| AOP support | Limitations | Full |
| Internationalization | No | Yes |
| Environment abstraction | No | Yes |
In fact, always use ApplicationContext.
1.3 Bean Registration
// Cách 1: Stereotype Annotations (phổ biến nhất)
@Component // Generic component
@Service // Business logic
@Repository // Data access
@Controller // Web controller
@RestController // REST API controller
@Configuration // Configuration class
// Cách 2: @Bean method trong @Configuration
@Configuration
public class AppConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
@Bean
public RestClient restClient() {
return RestClient.builder()
.baseUrl("https://api.example.com")
.build();
}
}
2. Auto-Configuration Deep Dive
2.1 Mechanism of action
Auto-Configuration is based on assessment of conditions at startup time:
Application Start
│
▼
@SpringBootApplication
│
├── @EnableAutoConfiguration
│ │
│ ▼
│ META-INF/spring/
│ org.springframework.boot.autoconfigure.AutoConfiguration.imports
│ │
│ ▼
│ Evaluate @Conditional annotations
│ │
│ ├── @ConditionalOnClass → Class có trong classpath?
│ ├── @ConditionalOnMissingBean → Bean đã tồn tại chưa?
│ ├── @ConditionalOnProperty → Property có được set?
│ └── @ConditionalOnWebApplication → Có phải web app?
│ │
│ ▼
│ Create & Register Beans
│
▼
Application Ready
2.2 Example: DataSource Auto-Configuration
When you add spring-boot-starter-data-jpa and PostgreSQL driver:
// Spring Boot tự động detect và tạo configuration tương tự:
@AutoConfiguration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
Spring Boot will:
- Detection
DataSource.classin classpath →@ConditionalOnClass= true - Inspection not yet available
DataSourcebeans →@ConditionalOnMissingBean= true - Read properties
spring.datasource.*→ Create DataSource
2.3 View Auto-Configuration Report
# Bật debug mode để xem report
debug=true
./mvnw spring-boot:run
Output will display:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required classes
'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'
Negative matches:
-----------------
MongoAutoConfiguration:
- @ConditionalOnClass did not find required class
'com.mongodb.client.MongoClient'
2.4 Customize or Disable Auto-Configuration
// Exclude cụ thể
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
SecurityAutoConfiguration.class
})
public class MyApplication { }
// Hoặc trong properties
spring.autoconfigure.exclude=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
3. Application Properties & YAML
3.1 Properties vs YAML
application.properties:
server.port=8080
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=admin
spring.datasource.password=secret
spring.jpa.hibernate.ddl-auto=update
application.yaml (or .yml):
server:
port: 8080
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secret
jpa:
hibernate:
ddl-auto: update
Both formats are supported. YAML is more readable for nested configuration, but properties are more common.
3.2 Custom Properties
// Định nghĩa properties class
@ConfigurationProperties(prefix = "app")
public record AppProperties(
String name,
String version,
Security security
) {
public record Security(
String jwtSecret,
long jwtExpirationMs,
long refreshExpirationMs
) {}
}
# application.yaml
app:
name: My Application
version: 1.0.0
security:
jwt-secret: my-256-bit-secret-key-for-jwt-signing
jwt-expiration-ms: 3600000
refresh-expiration-ms: 86400000
// Enable và sử dụng
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class MyApplication { }
@Service
public class AuthService {
private final AppProperties appProperties;
public AuthService(AppProperties appProperties) {
this.appProperties = appProperties;
// appProperties.security().jwtSecret()
}
}
3.3 Property Sources & Priorities
Spring Boot reads properties from many sources with priority order:
1. Command line arguments (highest priority)
java -jar app.jar --server.port=9090
2. OS Environment variables
SERVER_PORT=9090
3. application-{profile}.properties
application-prod.properties
4. application.properties
src/main/resources/application.properties
5. @PropertySource annotations
6. Default properties (lowest priority)
4. Profiles — Configuration according to environment
4.1 Definition of Profiles
# application.yaml (default, mọi môi trường)
spring:
application:
name: my-app
---
# application-dev.yaml
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:postgresql://localhost:5432/mydb_dev
jpa:
show-sql: true
---
# application-prod.yaml
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:postgresql://db-prod:5432/mydb_prod
jpa:
show-sql: false
4.2 Activate Profile
# Cách 1: Command line
java -jar app.jar --spring.profiles.active=prod
# Cách 2: Environment variable
SPRING_PROFILES_ACTIVE=prod java -jar app.jar
# Cách 3: Trong application.properties
spring.profiles.active=dev
# Cách 4: Trong IDE
# Run Configuration → Environment variables → SPRING_PROFILES_ACTIVE=dev
4.3 Profile-specific Beans
@Configuration
public class StorageConfig {
@Bean
@Profile("dev")
public StorageService localStorageService() {
return new LocalStorageService("/tmp/uploads");
}
@Bean
@Profile("prod")
public StorageService s3StorageService() {
return new S3StorageService();
}
}
5. Externalized Configuration Best Practices
5.1 No hardcode secrets
# SAI - Không bao giờ commit secrets
spring:
datasource:
password: my-real-password
# ĐÚNG - Dùng environment variables
spring:
datasource:
password: ${DB_PASSWORD}
5.2 Using .env files for local development
# .env (thêm vào .gitignore!)
DB_PASSWORD=local-dev-password
JWT_SECRET=dev-secret-key
5.3 Validation for properties
@ConfigurationProperties(prefix = "app")
@Validated
public record AppProperties(
@NotBlank String name,
@NotBlank String version,
@Valid Security security
) {
public record Security(
@NotBlank String jwtSecret,
@Positive long jwtExpirationMs
) {}
}
The application will fail-fast when required properties are missing.
Summary
- IoC Container (ApplicationContext) manages the lifecycle of beans, supports Dependency Injection via constructor/setter/field injection
- Auto-Configuration automatically configures beans based on classpath dependencies and conditional annotations
- Spring Boot supports both
.propertiesand.yamlformat, Profiles allows configuration according to environment (dev/staging/prod)
Exercises
- Turn on
debug=truein application.properties, run the application and analyze the 5 auto-configurations that are matched (Positive matches) - Create
AppPropertiesrecord class with prefixapp, bind 3 custom properties from application.yaml and inject into a controller to display - Create 2 profiles (dev and prod) with different database URLs, run the application with each profile and verify log output