Understanding AOP in Spring: Concepts, Annotations, and Practical Examples
This article explains the fundamentals of Aspect‑Oriented Programming (AOP) in Spring, describes its core concepts and terminology, and provides step‑by‑step code examples for creating pointcuts, advices, and custom annotations, while also detailing the use of common AOP annotations such as @Pointcut, @Around, @Before, @After, @AfterReturning and @AfterThrowing.
AOP (Aspect Oriented Programming) is one of Spring's three core concepts and helps separate cross‑cutting concerns like permission checks, logging, and statistics from business logic.
1. Understanding AOP
Systemic requirements often lead to repetitive code scattered across many methods. By extracting these concerns into a separate aspect, developers can avoid redundancy and improve maintainability.
1.2 AOP System and Concepts
Where to cut in (pointcut)
When to cut in (before or after method execution)
What to do after cutting (advice)
Key terms: Pointcut: defines the join points (e.g., execution(...) or annotation(...)) Advice: the action performed at a join point (e.g., logging, validation) Aspect: a combination of pointcut and advice Joint point: a specific execution point, usually a method call Weaving: the process of applying the aspect to the target object
2. AOP Examples
2.1 First example – log before all GET requests
package com.mu.demo.advice;
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
public class LogAdvice {
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
private void logAdvicePointcut() {}
@Before("logAdvicePointcut()")
public void logAdvice() {
System.out.println("get请求的advice触发了");
}
} package com.mu.demo.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/aop")
public class AopController {
@GetMapping("/getTest")
public JSONObject aopTest() {
return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
}
@PostMapping("/postTest")
public JSONObject aopTest2(@RequestParam("id") String id) {
return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
}
}The console prints the message only for the GET endpoint, confirming the pointcut works.
2.2 Second example – custom annotation for permission checks
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionAnnotation {} package com.example.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Order(1)
public class PermissionFirstAdvice {
@Pointcut("@annotation(com.mu.demo.annotation.PermissionAnnotation)")
private void permissionCheck() {}
@Around("permissionCheck()")
public Object permissionCheckFirst(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("===================第一个切面===================:" + System.currentTimeMillis());
Object[] args = joinPoint.getArgs();
Long id = ((JSONObject) args[0]).getLong("id");
if (id < 0) {
return JSON.parseObject("{\"message\":\"illegal id\",\"code\":403}");
}
return joinPoint.proceed();
}
}Two advice classes can be ordered with @Order to control execution sequence.
3. AOP Related Annotations
3.1 @Pointcut
@Aspect
@Component
public class LogAspectHandler {
@Pointcut("execution(* com.mutest.controller..*.*(..))")
public void pointCut() {}
}Defines where the aspect applies, using either execution() or annotation() expressions.
3.2 @Around
@Around("permissionCheck()")
public Object permissionCheck(ProceedingJoinPoint joinPoint) throws Throwable {
// pre‑processing
Object[] args = joinPoint.getArgs();
// modify arguments if needed
return joinPoint.proceed(args); // or skip execution
}Provides full control over when and how the target method runs, allowing argument modification and return‑value alteration.
3.3 @Before
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) {
// log method name, package, request URL, IP, etc.
}Runs before the matched method, useful for logging or pre‑validation.
3.4 @After
@After("pointCut()")
public void doAfter(JoinPoint joinPoint) {
// post‑execution logging
}Executes after the method regardless of its outcome.
3.5 @AfterReturning
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result) {
// enhance or log the returned value
}Captures the method's return value for further processing.
3.6 @AfterThrowing
@AfterThrowing(pointcut = "pointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
// handle exceptions, log error details
}Invoked when the target method throws an exception, allowing centralized error handling.
By combining these annotations, developers can cleanly separate cross‑cutting concerns from business logic, improve code readability, and maintain a modular architecture.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
