Trong vòng đời quản lý Bean của Spring, giai đoạn khởi tạo (instantiation) là bước then chốt để tạo ra đối tượng thực tế. Tuy nhiên, framework cũng cung cấp cơ chế để can thiệp vào quá trình này thông qua giao diện InstantiationAwareBeanPostProcessor. Đặc biệt, phương thức postProcessBeforeInstantiation cho phép trả về một đối tượng proxy thay thế trước khi Spring tự thực hiện việc khởi tạo bean gốc.
Triển Khai Bean Mục Tiêu
Đầu tiên, chúng ta định nghĩa một lớp thành phần đơn giản đóng vai trò là bean cần được quản lý. Lớp này chứa logic nghiệp vụ cơ bản.
public class TargetComponent {
public void executeTask() {
System.out.println("Đang thực thi nhiệm vụ chính...");
}
}
Tùy Chỉnh Processors
Tiếp theo, tạo một lớp xử lý hậu kỳ (PostProcessor) implements InstantiationAwareBeanPostProcessor. Tại đây, chúng ta ghi đè phương thức postProcessBeforeInstantiation để kiểm soát quá trình tạo đối tượng.
public class CustomLifecycleProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("Bean: " + beanName + " - Gọi postProcessBeforeInstantiation");
// Kiểm tra xem bean hiện tại có phải là đối tượng mục tiêu không
if (beanClass == TargetComponent.class) {
// Trả về một instance mới, Spring sẽ sử dụng object này thay vì tự khởi tạo
return new TargetComponent();
}
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("Bean: " + beanName + " - Gọi postProcessAfterInstantiation");
return false;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean: " + beanName + " - Gọi postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean: " + beanName + " - Gọi postProcessAfterInitialization");
return bean;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.println("Bean: " + beanName + " - Gọi postProcessProperties");
return pvs;
}
}
Cấu Hình và Kiểm Thử
Cấu hình các bean trong file XML và chạy ứng dụng để quan sát luồng thực thi.
public class BootstrapApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
}
}
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="targetComponent" class="com.example.core.TargetComponent"/>
<bean id="customLifecycleProcessor" class="com.example.core.CustomLifecycleProcessor"/>
</beans>
Phân Tích Luồng Thực Thi
Khi container Spring khởi động, phương thức refresh sẽ được gọi. Quá trình đăng ký các BeanPostProcessor diễn ra thông qua registerBeanPostProcessors. Lưu ý rằng, chính đối tượng CustomLifecycleProcessor cũng cần được tạo ra. Vì vậy, tại thời điểm đăng ký, Spring sẽ phải khởi tạo bean này trước.
Trong quá trình tạo ra CustomLifecycleProcessor, phương thức resolveBeforeInstantiation được gọi. Tuy nhiên, lúc này chưa có BeanPostProcessor nào được đăng ký hoàn chỉnh để xử lý chính nó, nên phương thức này trả về null và quy trình tạo bean diễn ra bình thường.
Sau khi đăng ký xong các Processor, Spring chuyển sang giai đoạn finishBeanFactoryInitialization để khởi tạo các singleton bean còn lại, bao gồm TargetComponent. Lúc này, resolveBeforeInstantiation lại được gọi.
Chi Tiết Phương Thức resolveBeforeInstantiation
Xem xét mã nguồn của phương thức này để hiểu cơ chế quyết định:
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
// Kiểm tra xem bean này đã được xử lý trước khởi tạo chưa
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Đảm bảo lớp bean đã được phân giải
// Chỉ xử lý nếu bean không phải là synthetic và có đăng ký InstantiationAwareBeanPostProcessor
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
// Áp dụng các PostProcessor trước khi khởi tạo
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// Nếu có kết quả, áp dụng tiếp các PostProcessor sau khởi tạo
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
// Cập nhật trạng thái đã giải quyết
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
Điều kiện !Boolean.FALSE.equals(mbd.beforeInstantiationResolved) đảm bảo rằng logic này chỉ chạy nếu trạng thái chưa được xác định là false. Trường mbd.beforeInstantiationResolved lưu trữ thông tin về việc bean có cần được phân giải trước khi khởi tạo hay không.
Điều kiện !mbd.isSynthetic() loại trừ các bean nội bộ của Spring. Chỉ những bean do người dùng định nghĩa mới đi vào logic này. Phương thức hasInstantiationAwareBeanPostProcessors() kiểm tra xem container có đăng ký bất kỳ processor nào thuộc loại này không. Vì chúng ta đã đăng ký CustomLifecycleProcessor, điều kiện này thỏa mãn.
Bước tiếp theo là gọi applyBeanPostProcessorsBeforeInstantiation. Phương thức này lặp qua tất cả các BeanPostProcessor đã đăng ký:
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
Khi vòng lặp gặp CustomLifecycleProcessor, phương thức postProcessBeforeInstantiation do chúng ta viết sẽ được thực thi. Nếu phương thức này trả về một đối tượng khác null (ví dụ: instance của TargetComponent), Spring sẽ coi đây là bean hoàn chỉnh.
Khi đó, biến bean trong resolveBeforeInstantiation sẽ không còn là null. Spring tiếp tục gọi applyBeanPostProcessorsAfterInitialization để chạy các logic sau khởi tạo trên đối tượng proxy vừa tạo. Cuối cùng, trường mbd.beforeInstantiationResolved được set thành true và đối tượng này được trả về.
Kết quả là quy trình tạo bean tiêu chuẩn (bao gồm việc populate properties và gọi các phương thức init tiêu chuẩn) sẽ bị bỏ qua cho bean này. Spring sử dụng trực tiếp đối tượng được trả về từ postProcessBeforeInstantiation làm bean cuối cùng trong container.