Bài 1 đếm được 266 bean cho ba lớp của tôi. Bài này về nơi 263 bean còn lại đến từ.
Ý tưởng: đoán từ classpath
Spring Boot nhìn xem có những jar nào và quyết định cần gì:
Có spring-webmvc và Tomcat trên classpath → dựng máy chủ web nhúng, DispatcherServlet, bộ chuyển đổi JSON.
Có driver JDBC và thuộc tính spring.datasource.url → tạo DataSource với HikariCP.
Có spring-data-jpa → tạo EntityManagerFactory và quản lý giao dịch.
Đó là toàn bộ khái niệm "convention over configuration": bạn thêm một dependency, và mọi thứ tự nối vào.
Cơ chế: @Conditional
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
class DataSourceAutoConfiguration {
@Bean DataSource dataSource(...) { ... }
}
Bốn điều kiện hay gặp nhất:
@ConditionalOnClass — chỉ chạy khi lớp đó có trên classpath.
@ConditionalOnMissingBean — chỉ chạy khi bạn chưa tự khai bean đó. Đây là điều kiện quan trọng nhất: nó khiến mọi auto-configuration đều nhường bạn.
@ConditionalOnProperty — theo giá trị trong application.yml.
@ConditionalOnWebApplication — chỉ trong ứng dụng web.
Danh sách các lớp auto-configuration nằm trong tệp META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports của mỗi jar starter. Mở nó ra là thấy toàn bộ danh mục.
Xem Boot quyết định gì
java -jar app.jar --debug
Positive matches:
-----------------
AopAutoConfiguration matched:
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
số auto-config KHỚP : 143
số dòng KHÔNG khớp : hàng trăm
143 lớp auto-configuration khớp cho một ứng dụng chỉ có web và actuator.
Báo cáo chia bốn phần:
Positive matches — đã áp dụng, kèm lý do. Negative matches — không áp dụng, kèm điều kiện nào không thoả. Exclusions — bạn loại trừ tường minh. Unconditional classes — luôn chạy.
Phần Negative matches là phần hữu ích nhất khi gỡ lỗi. Câu hỏi "vì sao bean X không được tạo" gần như luôn có câu trả lời ở đó:
DataSourceAutoConfiguration:
Did not match:
- @ConditionalOnProperty (spring.datasource.url) did not find property 'url'
Rõ ràng hơn nhiều so với việc đoán.
Có cách gọn hơn nếu đã bật actuator: endpoint /actuator/conditions trả về cùng thông tin dưới dạng JSON.
Ba cách can thiệp
Khai bean của bạn — nhờ @ConditionalOnMissingBean, auto-configuration tự lùi:
@Bean
ObjectMapper objectMapper() {
return new ObjectMapper().registerModule(new JavaTimeModule());
}
Đây là cách sạch nhất, và cũng là lý do bạn hiếm khi phải tắt auto-configuration.
Chỉnh bằng thuộc tính — phần lớn auto-configuration đọc application.yml:
spring:
jackson:
default-property-inclusion: non_null
Cách này tốt hơn tự khai bean vì bạn giữ được mọi cấu hình mặc định khác.
Loại trừ hẳn:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
hoặc
spring:
autoconfigure:
exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Dùng khi có starter kéo theo thứ bạn không muốn — ví dụ spring-boot-starter-data-jpa trong module chỉ dùng để chia sẻ entity.
Thứ tự
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
Auto-configuration chạy sau bean do bạn khai, và giữa chúng có thứ tự khai bằng after và before. Đây là lý do bạn không cần lo về thứ tự — Boot đã sắp sẵn.
Cái giá
143 lớp điều kiện phải được đánh giá lúc khởi động, và đó là phần đáng kể trong 1,1 giây ở bài 1.
Boot có vài cách giảm:
Chỉ thêm starter thật sự cần. spring-boot-starter-web kéo hơn bốn mươi jar; nếu bạn không cần web thì đừng thêm.
spring-boot-starter-webflux nhẹ hơn web nếu bạn dùng mô hình phản ứng.
AOT và Native Image tính sẵn điều kiện lúc build — bài 58 sẽ đo.
Với test, @SpringBootTest nạp toàn bộ context nên chậm. Các lát cắt như @WebMvcTest chỉ nạp phần cần — bài 52 sẽ nói.
Tự viết auto-configuration
Nếu bạn làm thư viện dùng chung trong công ty, viết auto-configuration cho nó là đáng:
@AutoConfiguration
@ConditionalOnClass(KhachHangClient.class)
@ConditionalOnProperty(prefix = "cty.khachhang", name = "url")
@EnableConfigurationProperties(KhachHangProps.class)
public class KhachHangAutoConfiguration {
@Bean
@ConditionalOnMissingBean
KhachHangClient khachHangClient(KhachHangProps p) {
return new KhachHangClient(p.url());
}
}
Khai tên lớp vào META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
Nhớ @ConditionalOnMissingBean — nó cho người dùng thư viện quyền ghi đè, và đó là quy ước bất thành văn của hệ sinh thái Spring.
Thử ba mươi giây
java -jar app.jar --debug 2>&1 | grep -A3 'Did not match' | head -40
Đọc mười điều kiện đầu không khớp. Bạn sẽ thấy Boot đang cân nhắc những thứ mình không biết là tồn tại — và lần tới khi một bean "không hiểu sao không có", đây là chỗ tìm đầu tiên.
Ngày mai: đọc log khởi động Spring Boot — những dòng đáng đọc và những dòng nên tắt.