AOP (Aspect-Oriented Programming) giúp tách cross-cutting concern (logging, security, transaction, metrics) ra khỏi business logic.
Khái niệm:
- Aspect — module chứa cross-cutting logic.
- Pointcut — biểu thức chọn method nào sẽ bị intercept.
- Advice — code chạy tại pointcut (@Before, @After, @Around, @AfterThrowing).
- JoinPoint — điểm thực thi cụ thể (method call).
@Aspect
@Component
class LoggingAspect {
// Pointcut: mọi method public trong package service
@Pointcut("execution(public * com.example.service.*.*(..))")
void serviceLayer() {}
// @Around — bao quanh method, kiểm soát cả before và after
@Around("serviceLayer()")
Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed(); // gọi method thật
} finally {
long elapsed = System.currentTimeMillis() - start;
log.info("{} completed in {}ms",
pjp.getSignature().getName(), elapsed);
}
}
// @Before — chỉ chạy trước method
@Before("@annotation(com.example.annotation.RequireAdmin)")
void checkAdmin(JoinPoint jp) {
if (!securityContext.isAdmin()) throw new AccessDeniedException("Admin required");
}
// @AfterThrowing — xử lý exception
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
void handleException(Exception ex) {
alertService.notify(ex.getMessage());
}
}Pointcut expressions phổ biến:
execution(* com.example.service.*.*(..)) // mọi method trong package
@annotation(Transactional) // method có annotation
within(com.example.repository.*) // mọi method trong class của package
args(Long, ..) // method có tham số đầu là LongLưu ý: Spring Boot tự bật AOP khi có spring-boot-starter-aop trên classpath — không cần @EnableAspectJAutoProxy.
AOP hoạt động qua proxy (giống @Transactional) → self-invocation không được intercept.
AOP (Aspect-Oriented Programming) separates cross-cutting concerns (logging, security, transactions, metrics) from business logic.
Concepts:
- Aspect — module containing cross-cutting logic.
- Pointcut — expression selecting which methods to intercept.
- Advice — code that runs at the pointcut (@Before, @After, @Around, @AfterThrowing).
- JoinPoint — the specific execution point (a method call).
@Aspect
@Component
class LoggingAspect {
// Pointcut: every public method in the service package
@Pointcut("execution(public * com.example.service.*.*(..))")
void serviceLayer() {}
// @Around — wraps the method, controls both before and after
@Around("serviceLayer()")
Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed(); // call the real method
} finally {
long elapsed = System.currentTimeMillis() - start;
log.info("{} completed in {}ms",
pjp.getSignature().getName(), elapsed);
}
}
// @Before — runs only before the method
@Before("@annotation(com.example.annotation.RequireAdmin)")
void checkAdmin(JoinPoint jp) {
if (!securityContext.isAdmin()) throw new AccessDeniedException("Admin required");
}
// @AfterThrowing — handles exceptions
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
void handleException(Exception ex) {
alertService.notify(ex.getMessage());
}
}Common pointcut expressions:
execution(* com.example.service.*.*(..)) // all methods in the package
@annotation(Transactional) // methods with an annotation
within(com.example.repository.*) // all methods in classes of the package
args(Long, ..) // method whose first param is LongNote: Spring Boot auto-enables AOP when spring-boot-starter-aop is on the classpath — no @EnableAspectJAutoProxy needed.
AOP works through proxies (like @Transactional) → self-invocation is not intercepted.