Skip to content

Spring Proxy & AOP Deep Dive — Cơ Chế “Magic” Đằng Sau @Transactional, @Cacheable, @Retryable

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ụ:

ConcernXuất hiện ở
Loggingmọi service
TransactionDB layer
Securityprotected API
Retryexternal API
Metricsmonitoring
Cacheread-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:

  1. JDK Dynamic Proxy
  2. 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

Published inAll

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *