Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Khái niệm khiến người mới Spring bối rối nhất thường là: ai tạo ra các đối tượng, và vì sao tôi không cần gõ new? Câu trả lời nằm ở Inversion of Control và Dependency Injection. Hiểu chúng, phần lớn "phép màu" của Spring sẽ trở nên rõ ràng.
Giả sử OrderService cần gửi thông báo. Cách ngây thơ là tự tạo bên trong:
public class OrderService { private final EmailSender sender = new EmailSender(); // dính chặt vào cài đặt cụ thể public void place(Order o) { // ... sender.send(o.getUserEmail(), "Đã đặt hàng"); }}Cách viết này khiến OrderService gắn chặt vào EmailSender. Muốn đổi sang gửi SMS, hoặc dùng bản giả khi kiểm thử, bạn phải sửa chính class đó.
Với DI, class chỉ khai báo nó cần gì, còn việc tạo và cung cấp được giao cho bên ngoài (container của Spring):
public interface Notifier { void notify(String to, String message);}@Servicepublic class EmailNotifier implements Notifier { public void notify(String to, String message) { /* gửi email */ }}@Servicepublic class OrderService { private final Notifier notifier; public OrderService(Notifier notifier) { // Spring tự tiêm this.notifier = notifier; } public void place(Order o) { notifier.notify(o.getUserEmail(), "Đã đặt hàng"); }}Giờ OrderService chỉ biết giao diện Notifier. Đổi cài đặt hay thay bản giả khi test không cần đụng đến nó.
Một bean là đối tượng do Spring tạo và quản lý. Container quét code tìm các class có chú thích như @Component, @Service, @Repository, @Controller, tạo bean tương ứng và nối chúng với nhau theo kiểu phụ thuộc. Bạn cũng có thể khai báo bean thủ công:
@Configurationpublic class AppConfig { @Bean public Clock clock() { return Clock.systemUTC(); }}Mặc định bean có phạm vi singleton: mỗi ứng dụng chỉ có một thể hiện. Vì vậy bean không nên giữ trạng thái riêng của từng người dùng trong các trường của nó.
final, dễ test, không thể tạo đối tượng thiếu phụ thuộc.@Autowired trên trường: ngắn gọn nhưng khó test và che giấu phụ thuộc, nên tránh trong code mới.Từ các phiên bản Spring gần đây, nếu class chỉ có một constructor thì không cần ghi @Autowired, như ví dụ ở trên.
Nếu có cả EmailNotifier và SmsNotifier cùng cài Notifier, Spring không biết chọn cái nào và báo lỗi. Bạn giải quyết bằng @Primary để đánh dấu mặc định, hoặc @Qualifier để chỉ rõ:
public OrderService(@Qualifier("smsNotifier") Notifier notifier) { this.notifier = notifier;}Vì phụ thuộc đi qua constructor, bạn có thể kiểm thử mà không cần khởi động Spring:
@Testvoid placeSendsNotification() { Notifier fake = mock(Notifier.class); OrderService service = new OrderService(fake); service.place(new Order("a@example.com")); verify(fake).notify("a@example.com", "Đã đặt hàng");}Test chạy trong mili giây, không cần database hay mạng. Đây chính là lý do thiết kế hướng DI được đánh giá cao.
Ngoài singleton, Spring còn các phạm vi khác như prototype (mỗi lần xin một thể hiện mới), request và session trong ứng dụng web. Bạn hiếm khi cần chúng, nhưng nên biết để hiểu khi nào một đối tượng được dùng chung. Bean cũng có vòng đời: được tạo, tiêm phụ thuộc, khởi tạo (có thể dùng @PostConstruct), sử dụng rồi hủy khi ứng dụng tắt (@PreDestroy). Hãy tránh đặt logic nặng hoặc gọi mạng trong lúc khởi tạo bean, vì lỗi ở đó sẽ khiến cả ứng dụng không khởi động được.
Một mẹo khi gỡ lỗi: bật log cấp DEBUG cho gói org.springframework hoặc dùng endpoint /actuator/beans để xem danh sách bean và quan hệ giữa chúng. Khi gặp lỗi "No qualifying bean" hay "circular dependency", nhìn vào danh sách này thường giúp bạn tìm ra nguyên nhân nhanh hơn nhiều so với đoán mò.
DI là cách thiết kế để các thành phần lỏng lẻo, dễ thay thế và dễ kiểm thử. Hãy ưu tiên constructor injection, phụ thuộc vào interface khi hợp lý, và để Spring lo phần lắp ráp. Một khi nắm được tư duy này, đọc code Spring sẽ dễ chịu hơn nhiều.
Muốn học bài bản và có lộ trình rõ ràng? Bạn có thể xem các khóa học trên codebem.com.
Nguyễn Đình Cường
0 comment