Spring AOP 的讲解

Spring AOP 详解


一、Spring AOP 是什么?

Spring AOP(面向切面编程) 是 Spring 框架的核心模块之一,旨在通过动态代理技术,将横切关注点(如日志、事务、权限校验)与核心业务逻辑分离,提升代码的模块化和可维护性。

  • 核心思想:将分散在多个类中的重复代码(横切逻辑)提取到独立的模块(切面),通过声明式配置将其“织入”到目标方法中。
  • 与 AspectJ 的关系:Spring AOP 是简化版的 AOP 实现,基于动态代理,适用于大多数常见场景;AspectJ 是更强大的 AOP 框架,支持编译时织入和更复杂的切入点表达式。

二、环境配置与依赖

1. 导入依赖

在 Maven 项目中,需添加以下依赖(Spring Boot 项目推荐使用 spring-boot-starter-aop):

<!-- Spring Boot AOP 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>


三、核心概念详解

在这里插入图片描述

1. 切面(Aspect)
  • 定义:封装横切逻辑的模块(如日志切面、事务切面)。
  • 实现:使用 @Aspect 注解标记类为切面,并通过 @Component 纳入 Spring 容器管理。
@Aspect
@Component
public class LogAspect {
    // 切入点和通知在此定义
}
2. 连接点(Join Point)
  • 定义:程序执行过程中的某个点(如方法调用、异常抛出、字段访问)。
  • 常见类型
    • 方法执行(Method execution
    • 方法调用(Method call
    • 异常处理(Exception handling
3. 切入点(Pointcut)
  • 定义:通过表达式匹配哪些连接点需要被切面处理指定执行的位置
  • 语法:使用 execution 表达式或注解匹配。
切入点表达式示例
// 匹配 com.example.service 包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}

// 匹配所有带有 @Transactional 注解的方法
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
4. 通知(Advice)
  • 定义:切面在特定连接点执行的具体逻辑。
  • 类型
通知类型注解执行时机
前置通知@Before目标方法执行前
后置通知@After目标方法执行后(无论是否异常)
返回通知@AfterReturning目标方法正常返回后
异常通知@AfterThrowing目标方法抛出异常后
环绕通知@Around包裹目标方法,可控制是否执行方法
通知示例
@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 前置通知
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("执行方法: " + methodName);
    }

    // 环绕通知(记录方法耗时)
    @Around("serviceMethods()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed(); // 执行目标方法
        long duration = System.currentTimeMillis() - start;
        System.out.println("方法耗时: " + duration + "ms");
        return result;
    }
}

四、自定义注解结合Spring AOP(通过注解可以指定在那些位置执行AOP的代码)

1. 定义自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PerformanceMonitor {}
2. 定义切面
@Aspect
@Component
public class PerformanceAspect {

    // 切入点:匹配所有带 @PerformanceMonitor 注解的方法
    @Pointcut("@annotation(com.example.annotation.PerformanceMonitor)")
    public void monitoredMethods() {}

    // 环绕通知
    @Around("monitoredMethods()")
    public Object measurePerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("方法执行耗时: " + (endTime - startTime) + "ms");
        return result;
    }
}
3. 在业务类中使用注解
@Service
public class UserService {

    @PerformanceMonitor
    public void processUsers() {
        // 业务逻辑
        System.out.println("处理用户数据...");
        try {
            Thread.sleep(1000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
4. 测试代码
@SpringBootTest
public class AopTest {

    @Autowired
    private UserService userService;

    @Test
    public void testPerformanceAspect() {
        userService.processUsers();
    }
}

输出结果

处理用户数据...
方法执行耗时: 1001ms

多个aop切面的时候,通知顺序
在这里插入图片描述

五、底层原理:动态代理

Spring AOP 通过 动态代理 实现切面逻辑的织入:

  1. JDK 动态代理:基于接口,要求目标类必须实现至少一个接口。
  2. CGLIB 代理:基于类,通过生成目标类的子类实现代理(无需接口)。

代理选择规则

  • 若目标类实现了接口,默认使用 JDK 动态代理。
  • 若目标类未实现接口,使用 CGLIB 代理。
  • 可通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用 CGLIB。

六、总结

Spring AOP 的核心价值

  • 解耦:将横切逻辑与业务代码分离。
  • 复用:通过切面集中管理通用逻辑。
  • 灵活:通过注解和表达式精准控制切入范围。

适用场景

  • 日志记录
  • 事务管理(如 @Transactional
  • 权限校验
  • 性能监控
  • 异常统一处理

掌握 Spring AOP 是构建高可维护性企业级应用的关键!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值