Trong thế giới Spring Boot, chúng ta thường dùng những annotation như:
@Transactional
@Cacheable
@Async
@Retryable
@PreAuthorize
Chỉ cần thêm annotation là mọi thứ “tự hoạt động”.
Nhưng đã bao giờ bạn tự hỏi:
Spring làm điều đó bằng cách nào?
Làm sao một method chỉ cần thêm @Transactional lại tự động:
- mở transaction
- commit
- rollback khi exception
mà không cần code thủ công?
Câu trả lời nằm ở:
Spring Proxy & AOP (Aspect Oriented Programming)
Bài viết này sẽ đi sâu vào cơ chế hoạt động thực tế của Spring Proxy, cách Spring AOP hoạt động bên trong framework, các pitfalls thường gặp và best practices dành cho Senior Engineer.
1. Vấn đề của OOP truyền thống
Giả sử chúng ta có service transfer tiền:
@Service
public class TransferService {
public void transfer(Long from, Long to, BigDecimal amount)
{
log.info("Transfer started");
validate(from, to, amount);
debit(from, amount);
credit(to, amount);
log.info("Transfer success");
}
}
Business logic rất sạch.
Nhưng thực tế system cần thêm:
- Logging
- Transaction
- Retry
- Security
- Metrics
- Audit
- Caching
Nếu viết trực tiếp:
public void transfer(...) {
log.info("Start");
transaction.begin();
try {
validate();
retry.execute(() -> {
security.check();
debit();
credit();
});
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw e;
}
}
Business logic bắt đầu bị polluted bởi technical concerns.
Đây gọi là:
Cross-cutting concerns
Những concern cắt ngang nhiều module.
Ví dụ:
| Concern | Xuất hiện ở |
|---|---|
| Logging | mọi service |
| Transaction | DB layer |
| Security | protected API |
| Retry | external API |
| Metrics | monitoring |
| Cache | read-heavy API |
Chúng ta muốn:
Tách technical concern khỏi business logic
Đó là lý do AOP ra đời.
2. AOP là gì?
AOP =
Aspect Oriented Programming
Một paradigm giúp inject logic trước/sau method execution.
Ví dụ:
Thay vì:
transfer() {
startTransaction();
business();
commit();
}
Ta viết:
@Transactional
transfer()
Spring sẽ tự wrap method call:
before()
business()
after()
Hoặc:
before
try
method
after returning
catch
after throwing
finally
after
3. Spring AOP hoạt động bằng cách nào?
Spring không modify source code.
Spring dùng:
Proxy Pattern
Khi bean được tạo, Spring không inject object thật.
Nó inject:
proxy object
Flow
Client
|
v
Proxy Object
|
before logic
|
Real Object
|
after logic
Ví dụ:
transferService.transfer();
Thực tế:
transferService (Proxy)
|
v
TransactionInterceptor
|
v
Real TransferService
4. Proxy Pattern là gì?
Proxy là object đứng giữa:
Caller
|
v
Proxy
|
v
Target
Proxy có thể:
- intercept call
- validate
- log
- cache
- retry
- transaction
Ví dụ đơn giản:
public interface PaymentService {
void pay();
}
Implementation:
public class PaymentServiceImpl
implements PaymentService {
@Override
public void pay() {
System.out.println("Real payment");
}
}
Proxy:
public class PaymentProxy
implements PaymentService {
private final PaymentService target;
public PaymentProxy(PaymentService target) {
this.target = target;
}
@Override
public void pay() {
System.out.println("Before");
target.pay();
System.out.println("After");
}
}
Output:
Before
Real payment
After
Spring AOP chính là automation version của idea này.
5. Hai loại Spring Proxy
Spring dùng:
- JDK Dynamic Proxy
- CGLIB Proxy
5.1 JDK Dynamic Proxy
Nếu bean có interface:
Spring dùng:
java.lang.reflect.Proxy
Ví dụ:
public interface TransferService {
void transfer();
}
Implementation:
@Service
public class TransferServiceImpl
implements TransferService {
@Transactional
public void transfer() {
}
}
Spring tạo:
Proxy implements TransferService
Runtime:
TransferService proxy
Không phải:
TransferServiceImpl
Internal Flow
Client
|
JDK Proxy
|
InvocationHandler
|
Target Method
InvocationHandler:
invoke(proxy, method, args)
intercept mọi method call.
Limitation
Chỉ proxy được:
interface methods
Nếu không có interface:
❌ không dùng được JDK proxy.
5.2 CGLIB Proxy
Nếu class không implement interface:
Spring dùng:
CGLIB
CGLIB tạo subclass runtime.
Ví dụ:
@Service
public class PaymentService {
@Transactional
public void pay() {
}
}
Spring runtime generate:
class PaymentService$$EnhancerBySpring
extends PaymentService {
@Override
public void pay() {
startTransaction();
super.pay();
commit();
}
}
Đây là lý do:
final method không proxy được
Vì subclass không override được:
final void transfer()
→ Spring AOP fail silently.
final class cũng không được
Ví dụ:
final class PaymentService
CGLIB không extend được.
6. Spring chọn proxy nào?
Default:
Có interface
→ JDK Proxy
Không interface
→ CGLIB
Force CGLIB:
spring:
aop:
proxy-target-class: true
Hoặc:
@EnableAspectJAutoProxy(
proxyTargetClass = true
)
7. AOP Terminology
Spring AOP có vài keyword cần nhớ.
Aspect
Business concern phụ.
Ví dụ:
LoggingAspect
@Aspect
@Component
public class LoggingAspect {
}
Advice
Logic execute tại một point.
Ví dụ:
Before
@Before(...)
After
@After(...)
Around
@Around(...)
Join Point
Điểm có thể intercept.
Trong Spring:
Method execution
Pointcut
Rule để match method.
Ví dụ:
execution(* com.bank.service.*.*(..))
Ý nghĩa:
mọi method trong package service
8. Ví dụ AOP Logging
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Around(
"execution(* com.bank.service..*(..))"
)
public Object logExecution(
ProceedingJoinPoint joinPoint
) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result =
joinPoint.proceed();
return result;
} finally {
long duration =
System.currentTimeMillis()
- start;
log.info(
"{} took {} ms",
joinPoint.getSignature(),
duration
);
}
}
}
Result:
transfer() took 45ms
Không cần modify business code.
9. @Transactional hoạt động thế nào?
Ví dụ:
@Transactional
public void transfer() {
debit();
credit();
}
Spring wrap method:
Pseudo:
transactionManager.begin();
try {
target.transfer();
transactionManager.commit();
} catch(Exception ex){
transactionManager.rollback();
throw ex;
}
Thực tế do:
TransactionInterceptor
handle.
Flow thực tế
Caller
|
Proxy
|
TransactionInterceptor
|
Real Method
|
commit/rollback
10. Vì sao self-invocation bị fail?
Một câu interview cực phổ biến.
Ví dụ:
@Service
public class UserService {
public void process() {
save();
}
@Transactional
public void save() {
}
}
Question:
save() có chạy transaction không?
Đáp án:
KHÔNG
Vì:
this.save()
không đi qua proxy.
Flow:
process()
|
this.save()
Proxy bị bypass.
Muốn hoạt động:
@Autowired
private UserService self;
self.save();
Hoặc tách sang service khác.
11. Common Pitfalls
11.1 private method
@Transactional
private void transfer()
❌ không chạy.
Vì proxy chỉ intercept:
public/protected method
11.2 final method
final void process()
❌ không override được.
11.3 constructor
Constructor không bị AOP intercept.
11.4 Exception rollback issue
Default:
Spring rollback:
RuntimeException
Không rollback:
checked exception
Ví dụ:
throws IOException
Fix:
@Transactional(
rollbackFor = Exception.class
)
12. Performance Impact
Spring proxy có overhead không?
Có.
Nhưng rất nhỏ.
Thường:
microseconds level
Không phải bottleneck.
Thứ tốn nhiều hơn:
- DB query
- network
- serialization
- remote call
Không nên prematurely optimize AOP.
13. Banking/Microservice Use Cases
Trong banking system:
Transaction
@Transactional
Transfer money.
Retry
Core banking timeout.
@Retryable
Security
@PreAuthorize
Check permission.
Metrics
Latency tracking.
API took 25ms
Audit
@Audit
Track sensitive action:
block card
change PIN
transfer money
14. Spring AOP vs AspectJ
Spring AOP:
✅ easy
✅ proxy based
✅ enough for 90% case
Nhưng chỉ intercept:
method execution
AspectJ:
✅ powerful
✅ compile-time weaving
✅ field access
✅ constructor interception
Nhưng:
❌ complex
Đa số enterprise app:
Spring AOP là đủ.
15. Interview Questions thường gặp
1. Spring AOP hoạt động như nào?
→ proxy based.
2. JDK proxy vs CGLIB?
JDK:
- interface based
CGLIB:
- subclass based
3. Vì sao @Transactional không chạy?
- self invocation
- private method
- final method
- checked exception
4. Vì sao self invocation fail?
Không đi qua proxy.
5. final method có AOP được không?
Không với CGLIB.
6. Constructor có AOP không?
Không.
Kết luận
Spring AOP không phải “magic”.
Đằng sau mọi annotation như:
@Transactional
@Retryable
@Cacheable
@Async
là:
Spring Proxy
Hiểu proxy giúp bạn:
- debug nhanh hơn
- tránh bug transaction
- hiểu self invocation issue
- optimize architecture
- trả lời interview Senior level
Một Senior Engineer không chỉ biết:
“dùng @Transactional”
mà cần hiểu:
Spring thực thi annotation đó như thế nào bên trong JVM
Be First to Comment