Hiểu Về Kiến Trúc Điều Khiển Đảo Ngược (IOC) và Tiêm Phụ Thuộc (DI)
Khi làm việc với Spring Framework, hai khái niệm nền tảng cần nắm rõ là IOC (Inversion of Control) và DI (Dependency Injection):
- IOC: Thay vì ứng dụng tự tạo và quản lý đối tượng, trách nhiệm này được chuyển giao cho container Spring. Đây là sự "đảo ngược" trong quyền kiểm soát — từ chủ động khởi tạo sang bị động nhận đối tượng đã được cấu hình sẵn.
- DI: Là cơ chế cụ thể để thực hiện IOC — thường thông qua setter method, constructor hoặc field injection. Spring tự động tiêm các phụ thuộc vào đối tượng theo cấu hình mà không cần mã lệnh new hay gán thủ công.
Một cách trực quan: thay vì viết Hello hello = new Hello();, bạn chỉ cần khai báo trong XML và yêu cầu Spring cung cấp — toàn bộ chu kỳ sống (instantiation, configuration, wiring) đều do framework đảm nhiệm.
Ví Dụ 1: Khởi Tạo Đối Tượng Đơn Giản Với XML Configuration
Tạo lớp MessageService trong gói org.example.model:
package org.example.model;
public class MessageService {
private String content;
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "MessageService{content='" + content + "'}";
}
}
File cấu hình application-context.xml trong thư mục src/main/resources:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="greetingService" class="org.example.model.MessageService">
<property name="content" value="Xin chào từ Spring!" />
</bean>
</beans>
Test class sử dụng ClassPathXmlApplicationContext:
import org.example.model.MessageService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationRunner {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application-context.xml");
MessageService service = (MessageService) ctx.getBean("greetingService");
System.out.println(service);
}
}
Ví Dụ 2: Tiêm Phụ Thuộc Động Giữa Các Lớp Dịch Vụ Và Lưu Trữ
Cấu trúc interface và implementation:
// Interface
public interface DataProvider {
String fetch();
}
// Các implementation cụ thể
public class MySqlProvider implements DataProvider {
public String fetch() { return "Dữ liệu từ MySQL"; }
}
public class PostgreProvider implements DataProvider {
public String fetch() { return "Dữ liệu từ PostgreSQL"; }
}
public class ReportService {
private DataProvider provider;
public void setProvider(DataProvider provider) {
this.provider = provider;
}
public void generate() {
System.out.println("Báo cáo: " + provider.fetch());
}
}
Cấu hình trong application-context.xml:
<bean id="mysqlAdapter" class="org.example.storage.MySqlProvider" />
<bean id="postgresAdapter" class="org.example.storage.PostgreProvider" />
<bean id="reportEngine" class="org.example.service.ReportService">
<property name="provider" ref="postgresAdapter" />
</bean>
Chạy thử nghiệm:
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
ReportService engine = (ReportService) context.getBean("reportEngine");
engine.generate(); // Kết quả sẽ thay đổi khi đổi ref trong XML
Nhờ cơ chế IOC/DI, việc thay đổi tầng lưu trữ (từ MySQL sang PostgreSQL) chỉ đòi hỏi chỉnh sửa cấu hình — không cần thay đổi bất kỳ dòng code nghiệp vụ nào.