AOP实战案例
Spring AOP在实际项目中应用广泛,以下展示几个典型场景。
一、操作日志记录
Java
// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
String module(); // 模块名
String operation(); // 操作描述
}
// 日志切面
@Aspect
@Component
@Slf4j
public class OperationLogAspect {
@AfterReturning(pointcut = "@annotation(opLog)", returning = "result")
public void recordLog(JoinPoint joinPoint, OperationLog opLog, Object result) {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
// 保存日志到数据库
SysLog sysLog = new SysLog();
sysLog.setModule(opLog.module());
sysLog.setOperation(opLog.operation());
sysLog.setMethod(className + "." + methodName);
sysLog.setParams(JSON.toJSONString(args));
sysLog.setCreateTime(LocalDateTime.now());
logService.save(sysLog);
log.info("操作日志: {} - {}", opLog.module(), opLog.operation());
}
@AfterThrowing(pointcut = "@annotation(opLog)", throwing = "ex")
public void recordError(JoinPoint joinPoint, OperationLog opLog, Exception ex) {
log.error("操作异常: {} - {}, 异常: {}", opLog.module(), opLog.operation(), ex.getMessage());
}
}
// 使用示例
@RestController
public class UserController {
@OperationLog(module = "用户管理", operation = "新增用户")
@PostMapping("/users")
public Result addUser(@RequestBody User user) {
return Result.success(userService.add(user));
}
}
二、性能监控
Java
// 性能监控注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PerformanceMonitor {
long threshold() default 1000; // 阈值(毫秒)
}
// 性能切面
@Aspect
@Component
@Slf4j
public class PerformanceAspect {
@Around("@annotation(monitor)")
public Object monitor(ProceedingJoinPoint pjp, PerformanceMonitor monitor) throws Throwable {
String methodName = pjp.getSignature().toShortString();
long start = System.currentTimeMillis();
try {
Object result = pjp.proceed();
return result;
} finally {
long elapsed = System.currentTimeMillis() - start;
if (elapsed > monitor.threshold()) {
log.warn("性能告警: {} 耗时 {}ms, 超过阈值 {}ms",
methodName, elapsed, monitor.threshold());
} else {
log.debug("方法执行: {} 耗时 {}ms", methodName, elapsed);
}
}
}
}
// 使用示例
@Service
public class OrderService {
@PerformanceMonitor(threshold = 500)
public Order createOrder(OrderRequest request) {
// 业务逻辑
}
}
三、权限校验
Java
// 权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String[] value();
}
// 权限切面
@Aspect
@Component
public class PermissionAspect {
@Autowired
private SecurityService securityService;
@Before("@annotation(requirePermission)")
public void checkPermission(JoinPoint jp, RequirePermission requirePermission) {
String[] permissions = requirePermission.value();
String userId = getCurrentUserId();
for (String permission : permissions) {
if (!securityService.hasPermission(userId, permission)) {
throw new PermissionDeniedException(
"无权限: " + permission);
}
}
}
private String getCurrentUserId() {
// 从SecurityContext获取当前用户ID
return SecurityContextHolder.getContext()
.getAuthentication().getName();
}
}
// 使用示例
@RestController
public class AdminController {
@RequirePermission("user:delete")
@DeleteMapping("/users/{id}")
public Result deleteUser(@PathVariable Long id) {
userService.delete(id);
return Result.success();
}
}
四、防重复提交
Java
// 防重注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicate {
int seconds() default 5; // 防重时间窗口
String key() default ""; // SpEL表达式
}
// 防重切面
@Aspect
@Component
@Slf4j
public class PreventDuplicateAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Around("@annotation(prevent)")
public Object prevent(ProceedingJoinPoint pjp, PreventDuplicate prevent) throws Throwable {
String key = generateKey(pjp, prevent);
String lockKey = "prevent:duplicate:" + key;
Boolean acquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", prevent.seconds(), TimeUnit.SECONDS);
if (Boolean.FALSE.equals(acquired)) {
throw new BusinessException("请勿重复提交");
}
try {
return pjp.proceed();
} finally {
redisTemplate.delete(lockKey);
}
}
private String generateKey(JoinPoint jp, PreventDuplicate prevent) {
String key = prevent.key();
if (StringUtils.hasText(key)) {
// 解析SpEL表达式
return parseSpEL(jp, key);
}
// 默认使用方法签名+参数hash
return jp.getSignature().toShortString() + Arrays.toString(jp.getArgs());
}
}
五、统一异常处理
Java
// 异常处理切面
@Aspect
@Component
@Slf4j
public class ExceptionAspect {
@AfterThrowing(pointcut = "execution(* com.example..service..*.*(..))",
throwing = "ex")
public void handleServiceException(JoinPoint jp, Exception ex) {
String method = jp.getSignature().toShortString();
log.error("业务异常 - 方法: {}, 异常: {}", method, ex.getMessage());
// 可选:发送告警通知
if (ex instanceof BusinessException) {
// 业务异常不告警
return;
}
alertService.sendAlert(method, ex);
}
}
要点总结
- 日志记录:使用@AfterReturning/@AfterThrowing捕获执行结果
- 性能监控:使用@Around环绕通知统计耗时
- 权限校验:使用@Before前置通知拦截无权限请求
- 防重复提交:结合Redis实现分布式锁
- 统一异常处理:集中处理Service层异常
📝 发现内容有误?点击此处直接编辑