The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect.
AOP helps to decouple cross-cutting concerns from the objects that they affect.
Aspect: A class that contains cross-cutting logic (e.g., logging, security).
Join Point: A point in the program execution — typically a method call — where an aspect can be applied.
Advice: The actual action taken by an aspect at a join point (e.g., code that runs before, after, or around a method).
Pointcut: An expression that defines where (which methods/classes) the advice should be applied.
Weaving: The process of applying aspects to target objects to create advised objects. This happens at runtime in Spring using proxies.
@Before: Before a method executes (useful for Logging, security checks)
@AfterReturning: After a method successfully returns (useful for Auditing, notifications)
@AfterThrowing: After a method throws an exception (useful for Exception handling, rollback)
@After: Runs after method — success or failure (useful for Cleanup, resource release)
@Around: Wraps method execution — before + after (useful for Transaction management, performance measurement)
Add dependency in Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Create an Aspect:
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// Define where this aspect applies
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// Before advice
@Before("serviceMethods()")
public void beforeAdvice() {
System.out.println("Before method execution...");
}
// After advice
@After("serviceMethods()")
public void afterAdvice() {
System.out.println("After method execution...");
}
// Around advice
@Around("serviceMethods()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before: " + joinPoint.getSignature());
Object result = joinPoint.proceed();
System.out.println("After: " + joinPoint.getSignature());
return result;
}
}
When a Spring bean method (e.g., in com.example.service) is called,
Spring uses dynamic proxies to intercept the method call,
The defined advices (e.g., @Before, @After, etc.) execute automatically around that method.
Spring uses runtime weaving via JDK dynamic proxies or CGLIB.
This means aspects are applied dynamically when beans are created by the Spring container — not at compile time.
Example:
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods()
This means:
com.example.service → target package
* → any class
* → any method
(..) → any number or type of arguments
So, this automatically covers all methods in all classes under com.example.service.
👉 serviceMethods(){} — This method is not meant to contain code. It's just a marker — a named reference for a pointcut expression.
Example:
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
This would target only methods annotated with @Transactional.
ProceedingJoinPoint represents the method call being intercepted — it gives you full control over:
The method signature (joinPoint.getSignature())
The arguments passed to the method (joinPoint.getArgs())
The ability to proceed with the method execution
Example:
@Around("serviceMethods()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before: " + joinPoint.getSignature());
// call the actual method
Object result = joinPoint.proceed();
System.out.println("After: " + joinPoint.getSignature());
return result;
}
joinPoint.proceed() actually executes the transferMoney() method.
Without calling proceed(), the target method won't run.
We can even modify arguments before calling proceed() if needed.
Think of it as a wrapper around the transferMoney() method.
Spring dynamically adds the aspect’s code into target classes at specific join points (like before or after a method).
Different types of weaving:
Compile-time weaving: During compilation. Requires special compilers (e.g., AspectJ).
Load-time weaving: When classes are loaded. Modifies bytecode before the class is used.
Runtime weaving (Spring AOP): When the bean is created in the Spring container. Uses proxies to intercept calls dynamically.
Spring uses runtime weaving — it doesn’t change the .class files. Instead, when Spring creates a bean, it wraps it in a proxy with hooks (interceptors) to run the aspect advice before/after the real method.