Understanding AOP in Spring Boot with Practical Code Examples
This article explains the fundamentals of Aspect‑Oriented Programming (AOP) in Spring, describes its core concepts such as pointcuts, advice, aspects, join points and weaving, and provides multiple Spring Boot examples—including simple @GetMapping logging, custom permission annotations, and the use of various AOP annotations—complete with full source code snippets.
1. Understanding AOP – AOP (Aspect Oriented Programming) is one of Spring's three core concepts alongside IoC and DI. It separates cross‑cutting concerns like permission checks, logging, and statistics from business logic, reducing code duplication and improving maintainability.
2. AOP System and Concepts – AOP performs three main tasks: where to cut (pointcut), when to cut (advice timing), and what to do after cutting. Key terms include Pointcut, Advice, Aspect, Join point, and Weaving.
3. AOP Examples
3.1 First Example – Log advice before all @GetMapping methods
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency> 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}");
}
}Running the application shows the console message only for the GET endpoint, confirming the pointcut works as intended.
3.2 Second Example – Custom @PermissionAnnotation with two ordered advices
@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 {
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();
}
} @Aspect
@Component
@Order(0)
public class PermissionSecondAdvice {
@Pointcut("@annotation(com.mu.demo.annotation.PermissionAnnotation)")
private void permissionCheck() {}
@Around("permissionCheck()")
public Object permissionCheckSecond(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
String name = ((JSONObject) args[0]).getString("name");
if (!"admin".equals(name)) {
return JSON.parseObject("{\"message\":\"not admin\",\"code\":403}");
}
return joinPoint.proceed();
}
} package com.example.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/permission")
public class TestController {
@RequestMapping(value = "/check", method = RequestMethod.POST)
@PermissionsAnnotation
public JSONObject getGroupList(@RequestBody JSONObject request){
return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}");
}
}When both advices are present, the @Order annotation determines execution order (lower number runs first). Tests show the second advice runs before the first when @Order(0) is used.
4. AOP‑Related Annotations
@Pointcut defines where to intercept; examples use execution(* com.mutest.controller..*.*(..)) and @annotation(org.springframework.web.bind.annotation.PostMapping) .
@Around can execute code before and after the target method, modify arguments via ProceedingJoinPoint.proceed(Object[] args) , and replace return values.
@Before runs before the target method, often for logging request URLs, IPs, or method signatures.
@After runs after the target method, useful for post‑execution logging.
@AfterReturning captures the method’s return value for further processing.
@AfterThrowing handles exceptions thrown by the target method.
The full project source is available on GitHub (https://github.com/ThinkMugz/aopDemo). The article concludes with a call for feedback and promotion of the author’s public account.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.