Here is the original AOP post.

And here is couple words about correlation ID.

The code is in this repo.

What AOP is usually used for

Logging is one aspect AOP can tackle nicely.

Anothed good on is user authentication

@Aspect
@Component
class UserAuthenticationAspect {

    private val logger = LoggerFactory.getLogger(UserAuthenticationAspect::class.java)

    @Before("@annotation(fi.kranu.servicea.aop.CustomAuthentication)")
    fun authenticate() {
        val request = (RequestContextHolder.getRequestAttributes() as? ServletRequestAttributes)?.request
        val userId = request?.getHeader("X-User-Id")

        if (userId.isNullOrBlank()) {
            logger.warn("Authentication failed: X-User-Id header is missing")
            throw UnauthorizedException()
        } else if (!userId.equals("dirty-hack")) {
            // This is just a dirty hack for testing purposes. Don't do this in production.
            logger.warn("Wrong secret")
            throw UnauthorizedException()
        }

        logger.info("User $userId authenticated successfully")
    }
}

Example 1. Transaction Management

One of the most important and widely used applications of AOP.

It ensures that database operations are automatically wrapped in a transaction boundary.

Typical use cases:

  • Atomic business operations
  • Automatic rollback on failure
  • Consistent data integrity

Example:

@Transactional
public void transferFunds(Account from, Account to, BigDecimal amount) {
    debit(from, amount);
    credit(to, amount);
}

Example 2. Performance Monitoring & Metrics

AOP is commonly used to measure execution time and feed observability systems.

Typical use cases:

  • API latency tracking
  • Slow query detection
  • SLA/SLO monitoring

Example:

@Timed("payment.processing.time")
public Payment processPayment(Request request) {
    ...
}

Example 3. Retry Logic for Resilience

Essential in distributed systems where downstream services can fail temporarily.

Typical use cases:

  • Network timeouts
  • Transient database failures
  • External API instability

Example:

@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 200))
public PaymentResponse callExternalService() {
    ...
}

Example 4. Rate Limiting / Throttling

Used to prevent abuse and control system load.

Typical use cases:

  • Public APIs
  • Multi-tenant SaaS quotas
  • DoS protection

Example:

@RateLimited("user-api")
public UserProfile getProfile(String userId) {
    ...
}

Example 5. Multi-Tenancy Enforcement

Ensures data isolation between tenants automatically.

Typical use cases:

  • SaaS applications
  • Tenant-specific data filtering
  • Automatic query scoping

Example:

@TenantScoped
public List<Order> findOrders() {
    ...
}

Example 6. Audit Trail Generation

Critical for compliance and traceability.

Typical use cases:

  • Financial systems
  • Healthcare applications
  • Regulatory reporting

Example:

@Auditable(action = "UPDATE_CUSTOMER")
public void updateCustomer(Customer customer) {
    ...
}

Example 7. Idempotency Enforcement

Prevents duplicate processing of the same request.

Typical use cases:

  • Payment processing
  • Order creation
  • Event handling

Example:

@Idempotent(key = "#request.transactionId")
public PaymentResult processPayment(PaymentRequest request) {
    ...
}

Example 8. Caching

Used to avoid repeated expensive computations or database calls.

Typical use cases:

  • Read-heavy endpoints
  • Reference data lookup
  • External API results

Example:

@Cacheable("products")
public Product getProduct(String id) {
    ...
}

Example 9. Distributed Tracing

Automatically creates trace spans for observability platforms.

Typical use cases:

  • Microservice request tracking
  • Performance bottleneck analysis
  • Dependency mapping

Example:

@NewSpan("order-processing")
public void processOrder(Order order) {
    ...
}

Example 10. Method Validation Enforcement

Ensures input constraints before method execution.

Typical use cases:

  • DTO validation
  • Service-level input checks
  • Contract enforcement

Example:

@Validated
public void createUser(@NotNull @Email String email) {
    ...
}

Example 11. Feature Flags / Conditional Execution

Allows dynamic behavior toggling without changing business logic.

Typical use cases:

  • A/B testing
  • Gradual rollout
  • Experimental features

Example:

@FeatureToggle("new-pricing-engine")
public Price calculatePrice(Order order) {
    ...
}

Example 12. Automatic Event Publishing

Triggers domain events after method execution.

Typical use cases:

  • Event-driven architecture
  • CQRS systems
  • Asynchronous workflows

Example:

@PublishEvent("ORDER_CREATED")
public Order createOrder(CreateOrderRequest request) {
    ...
}

Afterwords

When you are learning these, there is no need to test and see how you could do all of these. In my opinion you just need to practice couple of them and then the rest are easy enough to implement when needed. Learn ideas behind the system.