1. Spring AOP Annotation Explanation
@Before
Executed before the target method runs. Commonly used for logging, parameter validation, permission checks, etc.
When the defined pointcut expression is matched, Spring executes this method before calling the target method.
@Around
An around advice wraps the entire execution process of the target method.
By calling ProceedingJoinPoint.proceed() explicitly, it can add custom logic before and after the method execution.
This is the most flexible advice type — it can control whether the method executes, modify parameters, or process return values.
@After
Executed after the target method finishes, regardless of whether an exception occurred.
Similar to a finally block, it’s often used for resource cleanup or general logging.
@AfterReturning
Executed only when the target method returns normally.
If the method throws an exception, this advice will not run.
Often used to process the return value or log successful execution.
@AfterThrowing
Executed when the target method throws an exception.
Useful for logging exception details, sending alerts, or collecting error statistics.
@Pointcut
@Pointcut is used to extract pointcut expressions, which define where advices apply.
Instead of writing execution(...) repeatedly, you can define it once as a method and reuse it across multiple advices for better readability and maintainability.
Example:
@Pointcut("execution(* top.mygld.demo.service.impl..*(..))")
public void serviceMethods() {}
Then reuse it:
@Before("serviceMethods()")
@After("serviceMethods()")
2. Spring AOP Example (Using @Pointcut Extraction)
package top.mygld.demo.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class TestAspect {
// Extracted pointcut expression matching all methods under service.impl package
@Pointcut("execution(* top.mygld.demo.service.impl..*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void before() {
log.info("before ....");
}
@Around("serviceMethods()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("around ... before ....");
Object result = joinPoint.proceed();
log.info("around ... after ....");
return result;
}
@After("serviceMethods()")
public void after() {
log.info("after ....");
}
@AfterReturning("serviceMethods()")
public void afterReturning() {
log.info("afterReturning ....");
}
@AfterThrowing("serviceMethods()")
public void afterThrowing() {
log.info("afterThrowing ....");
}
}
Execution order when the method runs normally:
@Around → @Before → method execution → @AfterReturning → @After → @Around
Execution order when the method throws an exception:
@Around → @Before → method execution → @AfterThrowing → @After → @Around
Using these annotations together allows injecting logic at different stages — ideal for unified logging, monitoring, or security.
@Pointcut makes the structure cleaner by allowing multiple advices to share the same matching rules.
If different methods need their own pointcut expressions, they can be written separately, for example:
@AfterReturning("execution(* top.mygld.demo.dao.impl..*(..))")
public void afterReturning(){
log.info("afterReturning ....");
}
@AfterThrowing("execution(* top.mygld.demo.service.impl..*(..))")
public void afterThrowing(){
log.info("afterThrowing ....");
}
3. Multiple Advice Execution Order
In Spring AOP, the execution order of multiple advices follows these rules:
Within the same aspect class:
- Default natural order:
@Around → @Before → target method → @AfterReturning/@AfterThrowing → @After → @Around - If there are multiple advices of the same type (e.g., several
@Before), they execute in alphabetical order of method names. - For
@Before, smaller alphabetical order executes earlier; for@After, smaller alphabetical order executes later.
Between different aspect classes:
- By default, class names determine the order (alphabetical).
- You can control priority using
@Orderor implementingOrdered. - A smaller
@Ordervalue means higher priority — it executes@Beforeearlier and@Afterlater.
Example:
@Aspect
@Component
@Order(5)
public class LogAspect1 {}
@Aspect
@Component
@Order(7)
public class LogAspect2 {}
Execution overview:
Outer aspect @Around
↓
Inner aspect @Around
↓
@Before (outer to inner)
↓
Target method
↓
@AfterReturning / @AfterThrowing (inner to outer)
↓
@After (inner to outer)
↓
Inner @Around ends
↓
Outer @Around ends
This mechanism ensures predictable and precisely controlled AOP execution order when multiple aspects or advices exist.
4. Pointcut Expressions
execution
execution matches based on method return type, package name, class name, method name, and parameters.
Syntax:
execution(modifier? return-type package.class.?method(parameters) throws exception?)
Parts marked with ? are optional.
- Modifier: optional (e.g., public, protected)
- Package/class: optional but recommended
throws: optional (refers to declared exceptions, not runtime ones)
* matches any single element — return type, package, class, method, or parameter.
execution(* top.*.service.*.update*(*))
.. matches multiple consecutive elements — nested packages or any number of parameters.
execution(* top.mygld..UserService.*(..))
annotation
Matches methods annotated with a specific annotation. Example annotation:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogOperation {}
Applied to a method:
@LogOperation
@Override
public List<User> findAll() {
return userDao.findAll();
}
Used in an aspect:
@Before("@annotation(top.mygld.demo.anno.LogOperation)")
public void before(){
log.info("before");
}
In IDEs like IntelliJ, the m icon can confirm the match visually.
5. JoinPoint
In Spring, JoinPoint abstracts a connection point, allowing access to method execution details such as class name, method name, and arguments.
For @Around, use ProceedingJoinPoint;
for the other four types, use JoinPoint, which is the parent of ProceedingJoinPoint.
Example:
@Before("@annotation(top.mygld.demo.anno.LogOperation)")
public void before(JoinPoint jp){
log.info("before");
// 1. Get target object
Object target = jp.getTarget();
log.info("target: {}", target);
// 2. Get target class
String name = target.getClass().getName();
log.info("name: {}", name);
// 3. Get target method
String methodName = jp.getSignature().getName();
log.info("methodName: {}", methodName);
// 4. Get method arguments
Object[] args = jp.getArgs();
log.info("args: {}", args);
}
ProceedingJoinPointcan execute the target method, whileJoinPointcannot. The former is a subclass of the latter.