彻底搞懂Spring AOP:概念与实战

最近咨询的学员较多,其实这个也是老生常谈的问题,但是往往在面试求职环节,问的居多,尤其是工作1~3年的同学,很多人是会做,说不上来,今天与大家一起聊聊;

我来详细讲讲对Spring AOP的理解,包括概念、实现方式和具体例子。

一、什么是AOP?

AOP(面向切面编程)是一种编程范式,用于将横切关注点(如日志、事务、安全等)与核心业务逻辑分离的技术。

核心思想:

  • 关注点分离:将系统的通用功能(横切关注点)从业务逻辑中抽离
  • 模块化:通过切面将这些通用功能模块化,便于复用和维护
  • 解耦:避免业务代码中混入大量重复的通用代码

二、AOP的核心概念

概念 说明 类比
Aspect(切面) 横切关注点的模块化,如日志切面、事务切面 一个专门的"功能模块"
Join Point(连接点) 程序执行过程中的某个点,如方法调用、异常抛出 可以被增强的"时机点"
Pointcut(切点) 匹配连接点的表达式,定义在哪些地方应用切面 "筛选器",选择哪些连接点要增强
Advice(通知) 在特定连接点执行的动作 增强的具体"行为"
Weaving(织入) 将切面应用到目标对象创建代理对象的过程 "植入"增强逻辑的过程

三、Advice的类型

java 复制代码
// 1. Before:方法执行前
@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice() {
    System.out.println("方法执行前");
}

// 2. AfterReturning:方法正常返回后
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", 
                returning = "result")
public void afterReturning(Object result) {
    System.out.println("方法返回: " + result);
}

// 3. AfterThrowing:方法抛出异常后
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", 
               throwing = "ex")
public void afterThrowing(Exception ex) {
    System.out.println("方法异常: " + ex.getMessage());
}

// 4. After(Finally):方法执行后(无论成功还是异常)
@After("execution(* com.example.service.*.*(..))")
public void afterAdvice() {
    System.out.println("方法执行结束");
}

// 5. Around:环绕通知(最强大)
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
    // 方法执行前
    System.out.println("开始执行: " + joinPoint.getSignature());
    
    long start = System.currentTimeMillis();
    Object result = joinPoint.proceed();  // 执行目标方法
    
    // 方法执行后
    long end = System.currentTimeMillis();
    System.out.println("执行耗时: " + (end - start) + "ms");
    
    return result;
}

四、具体例子:日志切面

场景:为所有Service方法添加执行日志和耗时统计

java 复制代码
@Component
@Aspect
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    // 定义切点:匹配所有Service包下的方法
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void serviceLayer() {}
    
    // 环绕通知:记录方法执行耗时
    @Around("serviceLayer()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String className = signature.getDeclaringType().getSimpleName();
        String methodName = signature.getName();
        
        long start = System.currentTimeMillis();
        
        try {
            logger.info("[开始执行] {}.{}() 参数: {}", 
                       className, methodName, Arrays.toString(joinPoint.getArgs()));
            
            Object result = joinPoint.proceed();
            
            long end = System.currentTimeMillis();
            logger.info("[执行成功] {}.{}() 耗时: {}ms 结果: {}", 
                       className, methodName, (end - start), result);
            
            return result;
        } catch (Exception e) {
            long end = System.currentTimeMillis();
            logger.error("[执行异常] {}.{}() 耗时: {}ms 异常: {}", 
                        className, methodName, (end - start), e.getMessage(), e);
            throw e;
        }
    }
    
    // 方法执行前的通知
    @Before("serviceLayer() && @annotation(org.springframework.transaction.annotation.Transactional)")
    public void logBeforeTransactionalMethod(JoinPoint joinPoint) {
        logger.debug("准备执行事务方法: {}", joinPoint.getSignature());
    }
}

五、实际应用场景

1. 事务管理(Spring最经典的AOP应用)

java 复制代码
@Service
public class OrderService {
    
    @Transactional
    public void createOrder(Order order) {
        // 业务逻辑
        orderDao.save(order);
        inventoryService.reduceStock(order);
        // 如果出现异常,整个事务会回滚
    }
}

2. 权限控制

java 复制代码
@Aspect
@Component
public class SecurityAspect {
    
    @Before("@annotation(RequiresPermission)")
    public void checkPermission(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        RequiresPermission annotation = signature.getMethod()
            .getAnnotation(RequiresPermission.class);
        
        String permission = annotation.value();
        if (!SecurityContext.hasPermission(permission)) {
            throw new UnauthorizedException("缺少权限: " + permission);
        }
    }
}

// 使用
@RequiresPermission("order:create")
public void createOrder(Order order) {
    // 需要权限的业务逻辑
}

3. 缓存切面

java 复制代码
@Aspect
@Component
public class CacheAspect {
    
    @Around("@annotation(cacheable)")
    public Object cacheResult(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
        String key = generateCacheKey(joinPoint);
        Object cachedValue = cache.get(key);
        
        if (cachedValue != null) {
            return cachedValue;
        }
        
        Object result = joinPoint.proceed();
        cache.put(key, result, cacheable.ttl());
        
        return result;
    }
}

六、Spring AOP的实现原理

底层技术:

  1. JDK动态代理(默认,针对接口)
  2. CGLIB动态代理(针对类,当目标对象没有实现接口时)

织入时机:

  1. 编译时织入(AspectJ)
  2. 类加载时织入(AspectJ)
  3. 运行时织入(Spring AOP采用的方式)

七、使用建议

优点:

减少重复代码 :横切关注点只需编写一次 ✅ 提高可维护性 :业务逻辑更清晰 ✅ 灵活配置 :通过注解或XML配置切面 ✅ 易于测试:切面可以独立测试

注意事项:

⚠️ 性能开销 :代理调用比直接调用稍慢 ⚠️ 仅方法拦截 :Spring AOP只能拦截方法,不能拦截字段访问 ⚠️ 自调用问题 :同一类内方法互相调用,AOP可能失效 ⚠️ 理解复杂度:需要理解代理机制和AOP概念

总结

Spring AOP通过动态代理技术,在不修改原有代码的情况下,为程序添加额外的功能。它特别适合处理横切关注点,如日志、事务、安全、缓存等。理解AOP的核心概念(切面、切点、通知)和实际应用场景,能帮助我们编写更清晰、更易维护的代码。

相关推荐
用户84913717547162 小时前
实战复盘:10W+ QPS 秒杀架构演进(Redis Lua + 分片 + RabbitMQ)
java·架构·设计
aiopencode2 小时前
Fiddler抓包与接口调试实用指南,HTTPS配置、代理设置、API测试与性能优化全解析
后端
b***9102 小时前
idea创建springBoot的五种方式
java·spring boot·intellij-idea
t***82112 小时前
MySQL的底层原理与架构
数据库·mysql·架构
BD_Marathon2 小时前
【IDEA】常用快捷键【上】
java·ide·intellij-idea
BD_Marathon2 小时前
【IDEA】工程与模块的管理
java·ide·intellij-idea
tgethe2 小时前
MybatisPlus基础部分详解(中篇)
java·spring boot·mybatisplus
core5122 小时前
【Java AI 新纪元】Spring AI 深度解析:让 Java 开发者无缝接入大模型
java·人工智能·spring·ai
Y***89082 小时前
Spring Boot的项目结构
java·spring boot·后端