Quy tắc gốc của Spring AOP:
- Target implement ít nhất một interface → dùng JDK dynamic proxy, proxy chỉ expose các interface đó.
- Target không implement interface nào → dùng CGLIB, tạo runtime một subclass của chính class đó.
Ép dùng CGLIB bằng proxyTargetClass = true (@EnableAspectJAutoProxy(proxyTargetClass = true)). Spring Boot đặt sẵn spring.aop.proxy-target-class=true, nên mặc định trong ứng dụng Boot gần như luôn là CGLIB.
Hệ quả cần nhớ:
- JDK proxy: ép kiểu về class cụ thể sẽ ném ClassCastException; chỉ method khai báo trong interface mới được advise. Đây là lý do @Autowired theo interface là an toàn hơn.
- CGLIB: class final không proxy được, method final không advise được, method private cũng vậy. Constructor của target không được gọi lại vì proxy tạo qua Objenesis, nên đừng đặt logic khởi tạo trong constructor rồi trông chờ proxy chạy nó.
@Service
public final class OrderService { } // CGLIB cannot subclass -> proxy creation fails