Thẻ <bean> và Các Thuộc Tính Cốt Lõi
Trong cấu hình XML của Spring, thẻ <bean> dùng để khai báo một đối tượng được quản lý bởi IoC Container. Một số thuộc tính quan trọng bao gồm:
- id: Định danh duy nhất cho bean.
- name: Có thể đặt tên phụ (alias), tuy nhiên không khuyến khích sử dụng thường xuyên.
- class: Chỉ định lớp cụ thể để khởi tạo bean.
- scope: Xác định phạm vi tồn tại của bean:
- singleton (mặc định): Chỉ tạo duy nhất một thể hiện trong toàn bộ ứng dụng. Phù hợp với các thành phần như controller, service, repository hay các tiện ích chia sẻ.
- prototype: Mỗi lần yêu cầu sẽ tạo ra một thể hiện mới. Thường dùng cho các đối tượng mang dữ liệu nghiệp vụ như entity hoặc DTO.
- property: Dùng để tiêm giá trị hoặc tham chiếu vào thuộc tính của bean thông qua setter:
- name: Tên thuộc tính cần thiết lập.
- ref: Trỏ tới ID của một bean khác đã được khai báo.
Các Cách Khởi Tạo Bean
Sử dụng Constructor Không Tham Số
Spring mặc định gọi constructor rỗng để tạo đối tượng. Ví dụ:
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("Khởi tạo BookDaoImpl");
}
public void save() {
System.out.println("Lưu dữ liệu...");
}
}
Cấu hình XML tương ứng:
<bean id="bookDao" class="com.example.dao.BookDaoImpl"/>
Kết quả: Khi container khởi động, dòng "Khởi tạo BookDaoImpl" sẽ được in ra.
Sử dụng Factory Static
Áp dụng mẫu thiết kế Factory Method với phương thức tĩnh:
public class OrderDaoFactory {
public static OrderDao createOrderDao() {
return new OrderDaoImpl();
}
}
Trong tệp cấu hình:
<bean id="orderDao"
class="com.example.factory.OrderDaoFactory"
factory-method="createOrderDao"/>
Ứng dụng có thể lấy bean thông qua ApplicationContext.
Sử dụng Instance Factory
Khác với factory tĩnh, ở đây ta cần một thể hiện thực sự của lớp factory trước khi tạo bean:
public class OrderDaoFactory {
public OrderDao createOrderDao() {
return new OrderDaoImpl();
}
}
Cấu hình gồm hai bước:
<bean id="factory" class="com.example.factory.OrderDaoFactory"/>
<bean id="orderDao"
factory-bean="factory"
factory-method="createOrderDao"/>
Tối ưu hóa bằng FactoryBean (Từ Spring 2.0)
Để đơn giản hóa việc khởi tạo, Spring cung cấp giao diện FactoryBean<T>:
public class OrderDaoFactoryBean implements FactoryBean<OrderDao> {
@Override
public OrderDao getObject() throws Exception {
return new OrderDaoImpl();
}
@Override
public Class<?> getObjectType() {
return OrderDao.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
Chỉ cần khai báo đơn giản:
<bean id="orderDao" class="com.example.factory.OrderDaoFactoryBean"/>
Spring tự động hiểu rằng phải gọi getObject() để nhận đối tượng cuối cùng.
Vòng Đời Của Bean
Một bean trải qua các giai đoạn chính sau trong vòng đời:
- Khởi tạo đối tượng: Cấp phát vùng nhớ thông qua constructor hoặc factory.
- Gọi constructor: Thực thi logic khởi tạo ban đầu.
- Dependency Injection: Tiêm các phụ thuộc thông qua setter hoặc constructor.
- Khởi tạo (Initialization):
- Xử lý các interface Aware (như ApplicationContextAware).
- Thực thi tiền xử lý từ
BeanPostProcessor. - Thực hiện phương thức khởi tạo tùy chỉnh (qua
@PostConstructhoặc thuộc tínhinit-methodtrong XML). - Thực thi hậu xử lý từ
BeanPostProcessor.
- Hủy bỏ (Destruction): Chỉ xảy ra khi container đóng lại.
- Dùng
context.close()để đóng thủ công. - Hoặc đăng ký hook với
registerShutdownHook()để dọn dẹp tài nguyên.
- Dùng
Phụ Thuộc Và Tiêm Đối Tượng (Dependency Injection)
Tiêm Qua Setter
Yêu cầu lớp có trường và phương thức setter tương ứng:
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
Cấu hình XML:
<bean id="bookService" class="com.example.service.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
Tiêm Qua Constructor
Đảm bảo tính bất biến và dễ kiểm thử hơn:
public class BookServiceImpl {
private final BookDao bookDao;
private String appName;
public BookServiceImpl(BookDao bookDao, String appName) {
this.bookDao = bookDao;
this.appName = appName;
}
}
Cấu hình:
<bean id="bookService" class="com.example.service.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="appName" value="DemoApp"/>
</bean>
Tự Động Liên Kết (Autowiring)
Spring hỗ trợ tự động tìm và tiêm bean phù hợp mà không cần chỉ định tường minh.
Theo Kiểu Dữ Liệu (byType)
Hệ thống tìm kiếm bean nào khớp với kiểu dữ liệu yêu cầu. Nếu có nhiều hơn một, sẽ ném lỗi.
<bean id="bookService"
class="com.example.service.BookServiceImpl"
autowire="byType"/>
Khuyến nghị sử dụng cách này nếu môi trường kiểm soát tốt số lượng bean cùng kiểu.
Theo Tên (byName)
Spring tìm bean có id trùng với tên thuộc tính đang tiêm.
<bean id="bookService"
class="com.example.service.BookServiceImpl"
autowire="byName"/>
Ví dụ: Nếu có thuộc tính bookDao, nó sẽ tìm bean có id="bookDao".