Aspect-Oriented Programming with AspectJ

David Kumar Jan 2026
3 tabs
package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    // Pointcut for all methods in service package
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceMethods() {}

    // Pointcut for methods annotated with @Loggable
    @Pointcut("@annotation(com.example.demo.annotation.Loggable)")
    public void loggableMethods() {}

    // Before advice
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Calling method: {} with args: {}",
            joinPoint.getSignature().getName(),
            Arrays.toString(joinPoint.getArgs()));
    }

    // After returning advice
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("Method {} returned: {}",
            joinPoint.getSignature().getName(),
            result);
    }

    // After throwing advice
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        logger.error("Method {} threw exception: {}",
            joinPoint.getSignature().getName(),
            error.getMessage());
    }

    // Around advice for performance monitoring
    @Around("loggableMethods()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        try {
            Object result = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - start;

            logger.info("{} executed in {} ms",
                joinPoint.getSignature().toShortString(),
                executionTime);

            return result;
        } catch (Throwable throwable) {
            long executionTime = System.currentTimeMillis() - start;
            logger.error("{} threw exception after {} ms",
                joinPoint.getSignature().toShortString(),
                executionTime);
            throw throwable;
        }
    }
}
3 files · java Explain with highlit

AOP separates cross-cutting concerns from business logic. Aspects modularize logging, security, transactions, caching. @Aspect defines aspect classes. @Before, @After, @Around specify advice timing. Pointcuts select join points using expressions. @Around advice controls method execution fully. I use AOP for method timing, audit logging, exception handling. Aspects reduce code duplication across services. Spring AOP uses proxies; AspectJ uses bytecode weaving for more power. Proper pointcut design prevents performance issues. AOP enables declarative programming—annotations trigger cross-cutting behavior. Understanding AOP improves code organization and maintains separation of concerns. It's powerful but should be used judiciously to avoid obscuring program flow.