Clean • Professional
Spring AOP allows developers to separate cross-cutting concerns (like logging, security, transactions) from the main business logic. This improves modularity, readability, and maintainability of applications.
Cross-cutting concerns are features that affect multiple modules:
In simple words:
AOP = Add reusable behavior to your code without modifying the core business logic.
Aspect-Oriented Programming (AOP) is a programming paradigm that:
Spring AOP provides declarative aspect programming, allowing behaviors to run before, after, or around method execution.
| Term | Description |
|---|---|
| Aspect | A class containing cross-cutting logic (e.g., LoggingAspect) |
| Join Point | A point during program execution where an aspect can be applied (method call, exception, etc.) |
| Advice | Action taken by an aspect at a join point |
| Pointcut | Expression defining which join points the advice applies to |
| Weaving | Linking aspects with target objects (compile-time, load-time, or runtime) |
| Target Object | The actual object being advised |
| Proxy | Spring-generated object that wraps the target to apply aspects |
In Spring AOP, advice defines what action to take and when during the execution of a program. It is the code that runs at a join point (like a method call).

@BeforeExample:
@Before("execution(* com.example.service.*.*(..))")
publicvoidlogBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
@AfterExample:
@After("execution(* com.example.service.*.*(..))")
publicvoidlogAfter(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
@AfterReturningExample:
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
publicvoidlogAfterReturning(Object result) {
System.out.println("Method returned: " + result);
}
@AfterThrowingExample:
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
publicvoidlogAfterThrowing(Exception ex) {
System.out.println("Method threw exception: " + ex.getMessage());
}
@AroundExample:
@Around("execution(* com.example.service.*.*(..))")
public ObjectlogAround(ProceedingJoinPoint joinPoint)throws Throwable {
System.out.println("Before method: " + joinPoint.getSignature().getName());
Objectresult= joinPoint.proceed();// Execute the method
System.out.println("After method: " + joinPoint.getSignature().getName());
return result;
}
Spring AOP allows you to implement cross-cutting concerns (like logging, security, transactions) without modifying the business logic.
If you are using Spring Boot, add this dependency in pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Aspects define the cross-cutting logic and when it should run.
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
publicclassLoggingAspect {
// Pointcut for all methods in the service package
@Pointcut("execution(* com.example.service.*.*(..))")
publicvoidserviceMethods() {}
// Before advice
@Before("serviceMethods()")
publicvoidlogBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
// AfterReturning advice
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
publicvoidlogAfterReturning(Object result) {
System.out.println("Method returned: " + result);
}
}
Explanation:
@Aspect → marks this class as an aspect@Pointcut → defines which methods to intercept@Before → runs before matched method execution@AfterReturning → runs after method successfully returnsThis is the business logic class that will be advised by the aspect.
import org.springframework.stereotype.Service;
@Service
publicclassPaymentService {
public Stringpay(String user, double amount) {
System.out.println("Processing payment for " + user);
return"Payment Successful";
}
}
The controller is used to trigger the target service method, so we can see the aspect in action.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
publicclassPaymentController {
@Autowired
private PaymentService paymentService;
@GetMapping("/pay")
public Stringpay() {
return paymentService.pay("John",500);
}
}
Explanation:
@RestController → Marks this class as a REST controller.@Autowired → Injects the PaymentService bean (the target service).@GetMapping("/pay") → Exposes a GET endpoint to trigger the pay method./pay is called, Spring AOP automatically executes the aspect (LoggingAspect) before and after the method.When you access /pay endpoint:
Beforemethod: pay
Processing paymentfor John
Method returned: Payment Successful
Observation:
PaymentService logic.LoggingAspect, keeping business logic clean and maintainable.@Configuration
@EnableAspectJAutoProxy
publicclassAppConfig {}
@EnableAspectJAutoProxy instructs Spring to create proxies for beans with aspects.@Before advice executes.@AfterReturning, @After, or @AfterThrowing executes.An aspect in Spring AOP is a modular unit of cross-cutting concern (like logging, security, transactions). Spring supports different types of aspects depending on the instance management and scope.
Default in Spring; only one instance of the aspect exists per Spring container. All advised beans share the same aspect instance. Efficient and commonly used.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
publicclassSingletonLoggingAspect {
privateintcount=0;// Shared across all target beans
@Before("execution(* com.example.service.*.*(..))")
publicvoidlogBefore() {
count++;
System.out.println("Singleton Aspect executed. Count: " + count);
}
}
Behavior
SingletonLoggingAspect exists.count is shared among all advised beans.A new aspect instance is created for each advised target object. Useful when the aspect maintains state specific to each target instance. Less common and uses more memory.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Scope("prototype")// Create new instance for each advised object
publicclassPerTargetLoggingAspect {
privateintcount=0;// Unique to each target bean
@Before("execution(* com.example.service.*.*(..))")
publicvoidlogBefore() {
count++;
System.out.println("Per-Target Aspect executed. Count: " + count);
}
}
Behavior
PerTargetLoggingAspect is created for each target bean.count is specific to that bean.| Aspect Type | Instance Scope | When to Use |
|---|---|---|
| Singleton Aspect | One per container | Most cases (logging, security, metrics) |
| Per-Target Aspect | One per target object | Stateful aspects per bean |
| Feature | Spring AOP | AspectJ |
|---|---|---|
| Weaving | Runtime (proxy-based) | Compile-time / Load-time |
| Scope | Spring-managed beans only | Any class or method (including non-Spring classes) |
| Complexity | Simple to use | Advanced; more powerful but requires extra setup |
| Use Case | Logging, security, transactions, performance monitoring | Low-level bytecode manipulation, full AOP capabilities, complex cross-cutting concerns |
@Profile or @Conditional for environment-specific behavior@Around advice sparinglySpring AOP allows you to apply cross-cutting concerns declaratively, keeping business logic clean and reusable. Using aspects, advice, and pointcuts, Spring AOP automatically handles repetitive tasks like logging, security, and transaction management without modifying the target classes.