DEV Community

Sadiul Hakim
Sadiul Hakim

Posted on

Spring Boot AOP Programming

Spring AOP (Aspect-Oriented Programming) Tutorial

What is AOP?

Aspect-Oriented Programming (AOP) is a programming paradigm that helps you separate cross-cutting concerns (logic that affects multiple parts of an application) from your core business logic.

Example of cross-cutting concerns:

  • Logging
  • Security checks
  • Transaction management
  • Performance monitoring
  • Caching

Instead of writing the same code in multiple places, you define it once in an Aspect and let Spring automatically “weave” it into your application where needed.

How does AOP work in Spring?

Spring AOP uses proxies (dynamic proxies or CGLIB) to wrap beans and intercept method calls.

  • When a method on a bean is called, Spring checks if any advice (special code) should run before, after, or around that method.
  • The advice is executed, and then the target method runs.

This process is called weaving. In Spring, weaving is done at runtime (no need to modify class files manually).

When to use AOP?

Use AOP when you need to apply the same logic across multiple layers/modules:

  • Centralized logging (e.g., log every service method call)
  • Method execution time tracking
  • Security validation (check user roles before accessing methods)
  • Transactions (commit/rollback)

When NOT to use AOP?

❌ Don’t use AOP for business logic itself.
❌ Avoid AOP when simple delegation or inheritance is enough.
❌ Overusing AOP can make the code hard to trace because the logic is “hidden” in aspects instead of being visible in method calls.

Key AOP Terminologies

Term Meaning
Aspect A class that contains cross-cutting logic (e.g., logging aspect).
Join Point A point in program execution (method call, exception thrown, etc.) where you can apply advice.
Advice The actual action taken at a join point (before, after, around).
Pointcut An expression that selects join points (e.g., all methods in a package).
Weaving Linking aspects with application code (done at runtime in Spring).
Target Object The object whose method is being advised.
Proxy The object created by Spring AOP that wraps the target object.

What is a Join Point?

A Join Point is a specific point during execution of your program where AOP code can be applied.
In Spring AOP, a join point is always a method execution (not field access, constructor calls, etc. — those are supported only in full AspectJ, not Spring AOP).

Examples of Join Points:

  • When a method starts (@Before)
  • When a method returns (@AfterReturning)
  • When a method throws exception (@AfterThrowing)
  • When a method completes (@After)
  • Wrapping a method (@Around)

Types of Advice in Spring AOP

(1) Before Advice

Runs before the method executes.

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}
Enter fullscreen mode Exit fullscreen mode

(2) After Returning Advice

Runs after a method successfully returns.

@AfterReturning(
    pointcut = "execution(* com.example.service.*.*(..))",
    returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
    System.out.println("Method " + joinPoint.getSignature().getName() + " returned " + result);
}
Enter fullscreen mode Exit fullscreen mode

(3) After Throwing Advice

Runs when the method throws an exception.

@AfterThrowing(
    pointcut = "execution(* com.example.service.*.*(..))",
    throwing = "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
    System.out.println("Method " + joinPoint.getSignature().getName() + " threw exception: " + error);
}
Enter fullscreen mode Exit fullscreen mode

(4) After (Finally) Advice

Runs after method finishes (whether success or exception).

@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
    System.out.println("Method " + joinPoint.getSignature().getName() + " finished execution.");
}
Enter fullscreen mode Exit fullscreen mode

(5) Around Advice

Runs before and after the method. You can even control execution.

@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("Before execution: " + joinPoint.getSignature().getName());
    Object result = joinPoint.proceed(); // executes the target method
    System.out.println("After execution: " + joinPoint.getSignature().getName());
    return result;
}
Enter fullscreen mode Exit fullscreen mode

Example Project Setup

Step 1: Add Dependencies (Maven)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Step 2: Enable AOP

@SpringBootApplication
@EnableAspectJAutoProxy
public class AopDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(AopDemoApplication.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Service Class

@Service
public class UserService {
    public String getUserById(int id) {
        if (id == 0) throw new RuntimeException("User not found");
        return "User-" + id;
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Aspect Class (Cross-cutting)

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.demo.service.UserService.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    @After("execution(* com.example.demo.service.UserService.*(..))")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }

    @AfterReturning(
        pointcut = "execution(* com.example.demo.service.UserService.*(..))",
        returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("Returned: " + result);
    }

    @AfterThrowing(
        pointcut = "execution(* com.example.demo.service.UserService.*(..))",
        throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        System.out.println("Exception: " + error.getMessage());
    }

    @Around("execution(* com.example.demo.service.UserService.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around before: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed();
        System.out.println("Around after: " + joinPoint.getSignature().getName());
        return result;
    }

    @Before("@annotation(com.example.demo.annotation.Loggable)")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before annotated method: " + joinPoint.getSignature().getName());
    }
}
Enter fullscreen mode Exit fullscreen mode

What is a Pointcut?

A pointcut is an expression that tells Spring where (at which JoinPoints) an advice should be applied.

  • A JoinPoint = any method execution in Spring-managed beans.
  • A Pointcut = a filter that selects certain join points (based on method name, package, annotations, etc.).

So, Advice = What to do, and Pointcut = Where to do it.

How to Define a Pointcut?

In Spring AOP, you can:

  1. Inline Pointcut Expression – directly inside @Before, @After, etc.
  2. Reusable Pointcut Method – define it once with @Pointcut and reuse it across advices.

Example 1: Inline Pointcut (simple use case)

@Aspect
@Component
public class LoggingAspect {

    // Advice applied to all methods in UserService
    @Before("execution(* com.example.demo.service.UserService.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • execution(* com.example.demo.service.UserService.*(..)) =

    • * → any return type
    • UserService.*(..) → any method in UserService with any arguments

Here the pointcut is written inline inside @Before.

Example 2: Reusable Pointcut (best practice)

@Aspect
@Component
public class LoggingAspect {

    // Define a reusable pointcut for all service methods
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceLayerMethods() {}

    // Apply Before advice using the pointcut
    @Before("serviceLayerMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before service method: " + joinPoint.getSignature().getName());
    }

    // Apply After advice using the same pointcut
    @After("serviceLayerMethods()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After service method: " + joinPoint.getSignature().getName());
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • @Pointcut defines where advice applies.
  • serviceLayerMethods() is a method with no body (marker method).
  • Both @Before and @After use the same pointcut → DRY (Don’t Repeat Yourself).

Common Pointcut Expressions

// All methods in a package
execution(* com.example.service.*.*(..))

// Specific method
execution(* com.example.service.UserService.getUserById(..))

// Methods with parameters
execution(* com.example.service.UserService.*(String))

// All public methods
execution(public * *(..))

// Methods with @MyAnnotation
@annotation(com.example.MyAnnotation)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)