Giải Ngố: Inversion of Control (IoC)
Khi mới học lập trình (C, Java Core), chúng ta thường có thói quen "ôm show".Trong hàm main(), bạn quyết định khi nào tạo đối tượng, khi nào gọi hàm, khi nào huỷ đối tượng. Bạn là Vua, code của ta là lính
Nhưng khi bước chân vào thế giới Spring Boot, ta nghe đến Inversion of Control (IoC) - Đảo ngược điều khiển
Nghe có vẻ "nguy hiểm", nhưng thực chất bó chỉ là việc ta .... giao quyền quản lý cho người khác thôi
1. Ví dụ đời thường: Đi xe máy vs Đi taxi
Để hiểu IoC, hãy so sánh việc tự lái xe và đi taxi
Cách truyền thống (Không có IoC) - Tự lái xe
Ta muốn đi từ A đến B
- Ta phải tự tìm chìa khoá
- Tự dắt xe, tự nổ máy
- Tự đạp phanh, vặn ga, tránh ổ gà => Bạn nắm toàn quyền kiểm soát. Nếu ta mệt, xe không chạy
Cách hiện đại (Có IoC) - Đi Taxi (Grab/be)
Ta muốn đi từ A đến B
- Ta mwor app, đặt xe
- Tài xế đến đón. Ta leo lên xe ngồi lướt tóp tóp
- Tài xế lo việc lái, tránh xe , dừng đèn đỏ -> Quyền điều khiển đã bị đảo ngược từ ta sang tài xế. Bạn không cần lo về quy trình vận hành nữa, chỉ quan tâm kết quả
Trong lập trình : Bác tài xế là Framework (Spring Boot)
2. IoC trong Code : sự thay đổi quyền lực
Hãy xem sự khác biệt trong code Java
Cách cũ: Bạn là bá chủ (Developer Control)
Bạn viết code và tự tay quản lý mọi thứ
public class MyApp {
public static void main(String[] args) {
// 1. BẠN tự tạo kết nối database
MySQLConnection db = new MySQLConnection();
// 2. BẠN tự tạo service và đưa db vào
UserService service = new UserService(db);
// 3. BẠN tự gọi hàm chạy
service.run();
}
}
Vấn đề: thực ra chả sai đâu nhưng ta phải nhớ thứ tự tạo (tạo db trước, rồi mới tạo service). Code càng lớn, thì ta lại càng mệt vì pahir quản lý hàng nghìn các object
Cách mới: Spring là "Bá chủ" (Inversion of Control)
Bạn nhường quyền điều khiển cho Spring (cụ thể là IoC Container)
// Bạn chỉ cần đánh dấu: "Ê Spring, đây là Service nha"
@Service
public class UserService { ... }
// "Ê Spring, đây là Database nha"
@Component
public class MySQLConnection { ... }
// --- Khi chạy ứng dụng ---
// Spring (IoC Container): "Ok, tôi thấy ông cần UserService.
// Tôi sẽ tự tạo Database trước, rồi tự tạo UserService, rồi tự nối chúng lại."
Ta không còn viết dòng new UserService() nào cả. Spring tự làm hết. Quyền điều khiển luồng khởi tạo đã bị "Đảo ngược" từ tay bạn sang Framework
3. IoC Container - Bộ não của Spring
Spring có 1 bộ máy gọi là IoC Container. Nó có 2 nhiệm vụ
✔ Nhiệm vụ 1: Scan - Tìm ra object nào cần quản lý
Khi project khởi động, SPring đi quét (scan) toàn bộ code. Khi thấy các annotation này
Spring sẽ nói: "Ồ, đây là bean của tao, để tao quản lý"
✔ Nhiệm vụ 2: Tạo object + ghép dependency
Ví dụ ta có code sau
@Service
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) {
this.repo = repo;
}
}
Spring sẽ làm:
- Tạo UserRepository trước
- Tạo UserService(repo) và bơm inject cái repo vừa tạo vào
- Lưu 2 thằng này vào IoC Container
- Khi controller cần -> Spring đưa UserService ra dùng, Spring lôi UserService từ trong túi ra đưa luôn
Hành động Spring tự động nhét repo vào UserService chính là Dependency Injection - cách thức để thực hiện tư tưởng IoC
4. Vậy rốt cuộc "đảo ngược " ở đâu
Hãy nhìn vào bảng so sánh quy trình
| Trước IoC (Truyền thống) | Sau IoC (Spring Boot) |
|---|---|
| Bạn new Object | Spring tạo Object |
| Bạn new Dependency | Spring tạo Dependency |
| Bạn gắn Dependency vào Object | Spring tự bơm vào |
| Bạn quản lý thứ tự vào | Spring tự tính toán thứ tự |
Ta chỉ cần "đăng ký" class bằng annotation
Đảo ngược = quyền điều khiển thuộc về Spring thay vì bạn
5. Minh hoạ bằng flow cụ thể
Giả sử mô hình
UserController → UserService → UserRepository → DatabaseConnection
Khi ta bấm nút Start Spring Boot Application, điều gì xảy ra????
- Spring scan project: Spring quét toàn bộ project để tìm các class có annotation (@Controller, @Service....)
- Khởi tạo Bean: Spring nhận thấy sự phụ thuộc. Nó sẽ tạo từ dưới lên:
- Tạo DatabaseConnection
- Tạo UserRepository (và bơm connection vào)
- Tạo UserService (và bơm repository vào)
- Tạo UserController (và bơm service vào)
- Lưu trữ: Tất cả các object này (gọi là Bean) được lưu sẵn vào IoC Container (Mặc định là Singleton - chỉ tạo 1 lần duy nhất)
- Sẵn sang: Ứng dụng khởi động xong
- Xử lý Request: Khi có user gọi API (HTTP Request) tới Controller
- Spring không cần new lại nữa
- Spring lấy ngay UserController đã tạo sẵn ở bước 2 ra để xử lý
- Code chạy vèo vèo
Kết luận
Để tóm tắt về IoC trong Spring, hãy nhớ đến câu nói nổi tiếng của các đạo diễn Hollywood khi casting diễn viên:
"Don't call us, we'll call you." (Đừng gọi cho chúng tôi, khi nào cần chúng tôi sẽ tự gọi bạn.)
Trong lập trình cũng vậy: Bạn cứ viết Class đi, đừng lo việc khởi tạo. Khi nào cần, Spring sẽ tự gọi và cung cấp mọi thứ cho bạn.
Happy Coding! 🚀
All rights reserved