Spring AOP không dựa vào các cơ chế đặc biệt của JVM mà chủ yếu vận dụng kỹ thuật proxy để chèn logic bổ sung (advice) vào các điểm cắt (join point). Quá trình này được điều khiển bởi một chuỗi các thành phần hợp tác chặt chẽ — từ việc phân tích cấu hình XML đến việc tạo và quản lý proxy tại thời điểm chạy. Dưới đây là phân tích chi tiết về cơ chế hoạt động thực tế, tập trung vào luồng xử lý cốt lõi và cách thức hai chiến lược proxy (JDK và CGLIB) được lựa chọn và triển khai.
1. Khởi tạo cơ chế tự động tạo proxy qua thẻ cấu hình
Khi sử dụng thẻ <aop:aspectj-autoproxy> trong file cấu hình XML, Spring sẽ kích hoạt cơ chế tự động tạo proxy dựa trên chú giải @Aspect. Việc này bắt đầu từ việc đăng ký AspectJAutoProxyBeanDefinitionParser trong AopNamespaceHandler:
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("aspectj-autoproxy",
new AspectJAutoProxyBeanDefinitionParser());
}
}Trình phân tích này không tạo bean trực tiếp, mà chỉ đăng ký một BeanPostProcessor đặc biệt — AnnotationAwareAutoProxyCreator — vào container. Đây chính là "trung tâm điều phối" cho toàn bộ quá trình tạo proxy.
2. Vai trò then chốt của AnnotationAwareAutoProxyCreator
Lớp AnnotationAwareAutoProxyCreator kế thừa từ AbstractAutoProxyCreator và triển khai BeanPostProcessor. Nó can thiệp vào vòng đời của các bean thông qua hai phương thức chính:
postProcessBeforeInstantiation(): Được gọi trước khi khởi tạo instance. Thường dùng để xử lý các trường hợp đặc biệt nhưTargetSourcetùy chỉnh.postProcessAfterInitialization(): Được gọi sau khi bean đã được khởi tạo và các phương thứcinit(nếu có) đã hoàn tất — đây là điểm quan trọng nhất để quyết định có tạo proxy hay không.
Phương thức thứ hai chứa logic cốt lõi:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// Kiểm tra xem bean này có đang được proxy hóa hay không (để tránh vòng lặp)
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// Bỏ qua các bean thuộc hạ tầng (Advisor, Advice, v.v.)
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Lấy danh sách Advisor phù hợp với bean hiện tại
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(
bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// Đóng gói bean gốc thành TargetSource và tạo proxy
TargetSource targetSource = new SingletonTargetSource(bean);
return createProxy(bean.getClass(), beanName, specificInterceptors, targetSource);
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}3. Cơ chế khớp Advisor với bean mục tiêu
Việc xác định "advisor nào áp dụng được lên bean nào" được thực hiện bởi phương thức findAdvisorsThatCanApply(), dựa trên hai tiêu chí:
- Bộ lọc lớp (ClassFilter): Kiểm tra xem lớp của bean có thỏa mãn điều kiện (ví dụ:
@Within,@Target) hay không. - Bộ lọc phương thức (MethodMatcher): Duyệt qua tất cả các phương thức của lớp (bao gồm cả interface và lớp cha) để kiểm tra xem có phương thức nào khớp với biểu thức pointcut (ví dụ:
execution(* com.example.service.*.*(..))) hay không.
Quá trình này được tối ưu bằng bộ nhớ đệm (cache), đảm bảo mỗi cặp (beanClass, advisor) chỉ được đánh giá một lần.
4. Tạo proxy: Từ ProxyFactory đến AopProxy
Toàn bộ logic tạo proxy được đóng gói trong lớp ProxyFactory. Phương thức createProxy() thực hiện ba bước chính:
- Khởi tạo factory: Sao chép các cấu hình (như
proxy-target-class,expose-proxy) từAnnotationAwareAutoProxyCreator. - Cấu hình factory: Thêm danh sách
Advisorvà thiết lậpTargetSource. - Tạo proxy thực tế: Gọi
getProxy(), dẫn tới việc tạo một đối tượngAopProxycụ thể.
Việc lựa chọn giữa JDK proxy và CGLIB proxy được thực hiện trong DefaultAopProxyFactory:
public AopProxy createAopProxy(AdvisedSupport config) {
if (config.isOptimize() || config.isProxyTargetClass() ||
hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config); // Proxy interface
} else {
return new ObjenesisCglibAopProxy(config); // Proxy class
}
} else {
return new JdkDynamicAopProxy(config); // Default: proxy interface
}
}5. Triển khai proxy: Hai con đường khác nhau
5.1. JDK Dynamic Proxy
Khi mục tiêu là một interface, Spring sử dụng JdkDynamicAopProxy, một InvocationHandler. Toàn bộ logic xử lý lời gọi được dồn vào phương thức invoke():
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Xử lý các phương thức đặc biệt (equals, hashCode, toString...)
if (AopUtils.isEqualsMethod(method)) { ... }
if (AopUtils.isHashCodeMethod(method)) { ... }
// Lấy danh sách interceptor (advice) cho phương thức hiện tại
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// Không có advice → gọi trực tiếp phương thức mục tiêu
return AopUtils.invokeJoinpointUsingReflection(target, method, adaptedArgs);
} else {
// Có advice → xây dựng chuỗi interceptor và thực thi
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
return invocation.proceed();
}
}Chuỗi ReflectiveMethodInvocation hoạt động theo mô hình "chain of responsibility". Mỗi MethodInterceptor (ví dụ: AspectJAroundAdvice, AfterReturningAdvice) chịu trách nhiệm gọi proceed() để chuyển sang interceptor tiếp theo, và chèn logic của mình ở vị trí thích hợp (trước, sau, hoặc bao quanh).
5.2. CGLIB Proxy
Khi mục tiêu là một lớp cụ thể (không phải interface), Spring sử dụng ObjenesisCglibAopProxy. Cơ chế này tạo ra một lớp con tại runtime và ghi đè các phương thức cần advice. Quy trình chính gồm:
- Tạo một đối tượng
Enhancertừ thư viện CGLIB. - Thiết lập lớp cha (
setSuperclass) và các interface cần triển khai (setInterfaces). - Đăng ký một mảng
Callback(chủ yếu làMethodInterceptor) để xử lý các lời gọi phương thức. - Gọi
enhancer.create()để sinh lớp con và trả về instance.
Mỗi lời gọi phương thức trên proxy CGLIB sẽ kích hoạt phương thức intercept() của MethodInterceptor, nơi logic advice được thực thi và methodProxy.invokeSuper() được gọi để chuyển lời gọi tới phương thức gốc trong lớp cha.
6. Tổng kết luồng xử lý AOP
Toàn bộ quy trình có thể được tóm gọn thành năm giai đoạn liên tiếp:
- Khởi tạo: Đăng ký
AnnotationAwareAutoProxyCreatornhư mộtBeanPostProcessor. - Phát hiện: Trong quá trình khởi tạo bean,
BeanPostProcessorxác định các bean cần được "bọc" bằng proxy. - Phối hợp: Lọc danh sách
Advisorđể tìm những advisor phù hợp với lớp và phương thức của bean. - Xây dựng: Sử dụng
ProxyFactoryđể tạo mộtAopProxytương ứng (JDK hoặc CGLIB). - Thực thi: Khi client gọi phương thức trên bean, lời gọi được chuyển tới proxy, kích hoạt chuỗi các
MethodInterceptortrước khi cuối cùng đến phương thức đích.