Spring AOP 教程总结

1. AOP 简介

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,用于将横切关注点(如日志记录、事务管理、安全检查)从业务逻辑中分离,提高代码的可维护性和复用性。Spring AOP 是 Spring 框架的核心模块之一,通过动态代理(JDK 或 CGLIB)实现,与 IoC 容器无缝集成,支持注解和 XML 配置。

核心优势

  • 非侵入式:无需修改原有业务代码。
  • 解耦:将公共逻辑抽取为独立模块。
  • 适用场景:日志、性能监控、异常处理等。

2. AOP 核心概念

  • 切面(Aspect):模块化横切关注点,包含通知和切点(如一个日志类)。
  • 切点(Pointcut):定义何时触发切面(如匹配特定方法)。
  • 通知(Advice):切面的具体行为,包括:
    • @Before:方法执行前。
    • @After:方法执行后(无论成功或异常)。
    • @AfterReturning:方法正常返回后。
    • @AfterThrowing:方法抛异常后。
    • @Around:环绕通知,可控制方法执行(最强大)。
  • 连接点(Join Point):程序执行中的具体点(如方法调用)。
  • 织入(Weaving):将切面应用到目标对象的过程(Spring 通过代理实现)。

3. Spring AOP 配置

3.1 依赖引入(Maven)

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.23</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

3.2 XML 配置(spring-config.xml)

<beans xmlns:aop="http://www.springframework.org/schema/aop">
    <aop:aspectj-autoproxy />
    <bean id="loggingAspect" class="com.example.LoggingAspect" />
    <aop:config>
        <aop:aspect ref="loggingAspect">
            <aop:pointcut id="controllerMethods" expression="execution(* com.example.controller.*.*(..))" />
            <aop:before method="logBefore" pointcut-ref="controllerMethods" />
            <aop:after method="logAfter" pointcut-ref="controllerMethods" />
        </aop:aspect>
    </aop:config>
</beans>

3.3 注解配置(推荐)

在配置类中启用:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

4. 注解实现示例

4.1 切面类(LoggingAspect.java)

@Aspect
@Component  // 交给 Spring 管理
public class LoggingAspect {

    // 切点表达式:匹配 controller 包下所有公共方法
    @Pointcut("execution(* com.example.controller.*.*(..))")
    public void controllerMethods() {}

    @Before("controllerMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("【Before】方法执行前: " + joinPoint.getSignature().getName());
    }

    @After("controllerMethods()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("【After】方法执行后: " + joinPoint.getSignature().getName());
    }

    @Around("controllerMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();  // 执行目标方法
        long end = System.currentTimeMillis();
        System.out.println("【Around】执行时长: " + (end - start) + "ms");
        return result;
    }
}

4.2 目标类(TestController.java)

@Controller
public class TestController {
    @RequestMapping("/test")
    public String test() {
        // 业务逻辑
        return "success";
    }
}

5. 运行与测试

  • 启动 Spring 应用,调用 /test 接口。
  • 控制台输出:
    【Before】方法执行前: test
    【Around】执行时长: 5ms
    【After】方法执行后: test
    

注意

  • @Around 可获取返回值或抛异常处理。
  • 切点表达式支持 executionwithinargs 等。
  • JDK 代理需接口,CGLIB 支持无接口类(默认)。

6. 常见问题与扩展

  • 代理模式:接口类用 JDK,无接口用 CGLIB(配置 proxy-target-class="true")。
  • 性能:代理有轻微开销,适合非高并发场景。
  • 扩展:结合 AspectJ(更强大,但复杂);用于事务 @Transactional 底层即 AOP。
  • 最佳实践:切面优先注解,保持简单;测试时 mock 代理。

此总结基于 Spring 5.x 版本,适合初学者快速上手。实际项目中,推荐结合官方文档实践。

写文章用