Implementing Aspect-Oriented Programming
1. Implementing Logging Aspects
Example: Method entry/exit logger
@Aspect @Component
public class LoggingAspect {
@Around("@annotation(Loggable)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
var sig = pjp.getSignature().toShortString();
log.debug("--> {} args={}", sig, Arrays.toString(pjp.getArgs()));
long t0 = System.nanoTime();
try {
Object out = pjp.proceed();
log.debug("<-- {} ({} ms)", sig, (System.nanoTime() - t0)/1_000_000);
return out;
} catch (Throwable ex) {
log.warn("xx-- {} threw {}", sig, ex.toString());
throw ex;
}
}
}
Example: Micrometer timer
@Around("@annotation(io.micrometer.core.annotation.Timed)")
public Object timed(ProceedingJoinPoint pjp) throws Throwable {
return Timer.builder("method.timer")
.tag("method", pjp.getSignature().toShortString())
.register(meterRegistry)
.recordCallable(() -> {
try { return pjp.proceed(); } catch (Throwable t) { throw new RuntimeException(t); }
});
}
3. Implementing Security Aspects
| Use | Detail |
| @PreAuthorize | Spring Security AOP-based |
| Custom @RequiresScope | Check JWT scope before invoke |
| Audit before/after | Capture actor, args (sanitized) |
4. Implementing Transaction Aspects
| Detail | Note |
| Annotation | @Transactional woven by Spring |
| Self-invocation | Bypasses proxy → no TX |
| Propagation | REQUIRED, REQUIRES_NEW, NESTED |
| Rollback rules | RuntimeException by default |
5. Implementing Caching Aspects
| Annotation | Effect |
| @Cacheable | Return cached if present |
| @CachePut | Always execute, update cache |
| @CacheEvict | Remove on call |
| condition / unless | Conditional caching |
6. Implementing Audit Aspects
Example: @Auditable
@Around("@annotation(audit)")
public Object audit(ProceedingJoinPoint pjp, Auditable audit) throws Throwable {
var actor = SecurityContextHolder.getContext().getAuthentication().getName();
try {
Object out = pjp.proceed();
auditLog.write(actor, audit.action(), "SUCCESS", pjp.getArgs());
return out;
} catch (Throwable t) {
auditLog.write(actor, audit.action(), "FAILURE", pjp.getArgs());
throw t;
}
}
7. Implementing Validation Aspects
| Mechanism | Detail |
| @Validated on bean | Method-level Bean Validation |
| @Valid on params | Cascade into nested objects |
| Custom aspect | Domain-specific pre-checks |
8. Implementing Exception Handling Aspects
Example: @AfterThrowing
@AfterThrowing(pointcut = "execution(* com.acme.api..*(..))", throwing = "ex")
public void onError(JoinPoint jp, Throwable ex) {
metrics.counter("errors", "method", jp.getSignature().toShortString()).increment();
}
9. Designing Pointcut Expressions
| Designator | Example |
| execution | execution(* com.acme.service..*(..)) |
| within | within(com.acme.service..*) |
| @annotation | @annotation(Loggable) |
| @within | Class-level annotation |
| bean | bean(*Service) |
| args | args(java.lang.String) |
10. Implementing Around Advice
| Type | Detail |
| @Before | Run before; cannot prevent execution |
| @After | Always after (success or fail) |
| @AfterReturning | Only on success |
| @AfterThrowing | Only on exception |
| @Around | Wrap; can short-circuit, modify args/return |
11. Implementing Custom Annotations
Example: Marker annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimited {
String key() default "";
int permitsPerSecond() default 10;
}
12. Implementing Cross-Cutting Concerns
| Concern | AOP Fit |
| Logging / metrics | Excellent |
| Security checks | Excellent |
| Transactions | Excellent |
| Caching | Good |
| Business logic | Avoid (hides intent) |
Warning: Spring AOP uses proxies; calling another @Transactional/@Cacheable method on this bypasses the aspect.