Skip to content

讲解springAOP

Spring AOP 概述

  • 定义
  • Spring AOP(Aspect-Oriented Programming)是 Spring 框架提供的面向切面编程模块,用于将横切关注点(如日志、事务、权限)与业务逻辑分离,提高代码模块化和可维护性。
  • 核心思想
  • 通过切面(Aspect)在特定连接点(Join Point)上插入通知(Advice),实现功能增强。
  • 实现方式
  • 基于代理模式(动态代理或 CGLIB),在运行时为目标对象生成代理。

核心点

  • Spring AOP 通过代理实现非侵入式功能扩展,适合事务、日志等场景。

1. Spring AOP 核心概念

(1) 基本术语

  • Aspect(切面)
  • 封装横切逻辑的模块,如日志、事务。
  • 包含 Advice 和 Pointcut。
  • Join Point(连接点)
  • 可插入切面的程序执行点,如方法调用、异常抛出。
  • Spring AOP 只支持方法级别的连接点。
  • Advice(通知)
  • 切面在连接点执行的动作,类型包括:
    • Before:方法前执行。
    • After:方法后执行(无论成功或异常)。
    • AfterReturning:方法成功返回后。
    • AfterThrowing:方法抛异常后。
    • Around:环绕方法执行,可控制调用。
  • Pointcut(切点)
  • 定义哪些连接点应用通知的规则。
  • 例:execution(* com.example.service.*.*(..))
  • Target Object(目标对象)
  • 被切面增强的原始对象。
  • Proxy(代理)
  • Spring AOP 为目标对象生成的代理对象。
  • Weaving(织入)
  • 将切面应用到目标对象的过程,Spring AOP 是运行时织入

(2) 图示

[Client] --> [Proxy] --> [Advice: Before/After] --> [Target Method]

2. Spring AOP 实现原理

(1) 代理机制

  • 动态代理(JDK Proxy)
  • 若目标对象实现接口,使用 JDK 动态代理。
  • 生成接口的代理类,调用 InvocationHandler
  • CGLIB
  • 若无接口,使用 CGLIB 生成子类代理。
  • 通过字节码操作,直接调用目标方法。
  • 选择
  • Spring 自动选择,优先 JDK 代理。
  • 可强制 CGLIB:
<aop:config proxy-target-class="true"/>

(2) 运行时织入

  • 过程
  • Spring 容器启动,解析 AOP 配置(注解或 XML)。
  • 根据 Pointcut 匹配目标方法。
  • 生成代理对象,植入 Advice。
  • 客户端调用代理,触发 Advice 和目标方法。
  • 源码
  • org.springframework.aop 包。
  • 核心类:ProxyFactoryAopProxy

3. Spring AOP 使用方式

(1) 基于注解

  • 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 启用 AOP
@SpringBootApplication
@EnableAspectJAutoProxy // 启用注解 AOP
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • 定义切面
@Aspect
@Component
public class LogAspect {
    // 切点:匹配所有 Service 方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    // 前置通知
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before: " + joinPoint.getSignature().getName());
    }

    // 后置通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(Object result) {
        System.out.println("AfterReturning: " + result);
    }

    // 环绕通知
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around: start");
        Object result = joinPoint.proceed(); // 执行目标方法
        System.out.println("Around: end");
        return result;
    }
}
  • 目标类
@Service
public class UserService {
    public String getUser(int id) {
        return "User: " + id;
    }
}

(2) 基于 XML

  • 配置
<bean id="logAspect" class="com.example.LogAspect"/>
<aop:config>
    <aop:aspect ref="logAspect">
        <aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
        <aop:before pointcut-ref="serviceMethods" method="logBefore"/>
        <aop:after-returning pointcut-ref="serviceMethods" method="logAfterReturning" returning="result"/>
    </aop:aspect>
</aop:config>

(3) 运行结果

  • 调用 userService.getUser(1)
Around: start
Before: getUser
[执行 getUser]
AfterReturning: User: 1
Around: end

4. 常见应用场景

  • 日志记录
  • 记录方法调用、参数、耗时。
  • 事务管理
  • @Transactional 基于 AOP 实现。
  • 例:自动开启/提交事务。
  • 权限验证
  • 检查用户权限。
  • 例:结合 @PreAuthorize
  • 性能监控
  • 统计方法执行时间。
  • 异常处理
  • 统一捕获异常,转换格式。

示例:事务 AOP

@Service
public class OrderService {
    @Transactional
    public void createOrder() {
        // 数据库操作
    }
}
  • 内部:Spring 事务管理器通过 AOP 代理,自动处理 begin/commit/rollback

5. Spring AOP vs AspectJ

特性 Spring AOP AspectJ
织入方式 运行时(代理) 编译时/加载时(字节码)
连接点 仅方法 方法、字段、构造器等
性能 代理开销稍大 更高(直接修改字节码)
复杂度 简单(Spring 集成) 复杂(需额外工具)
场景 企业应用(事务、日志) 复杂切面(低级别控制)
  • Spring AOP 集成 AspectJ
  • 使用 AspectJ 的注解(如 @Pointcut),但仍用 Spring 代理。

6. 注意事项

  • 代理限制
  • 内部调用失效
    • 例:
@Service
public class UserService {
    @Transactional
    public void methodA() {
        methodB(); // 不触发事务
    }

    @Transactional
    public void methodB() {}
}
- 原因:内部调用不走代理。
- 解决:用 `AopContext.currentProxy()` 或注入自身。
  • 性能开销
  • 代理增加调用层,Around 通知需谨慎。
  • Pointcut 精确性
  • 过宽(如 execution(* *.*(..)))影响性能。

7. 优缺点

优点

  • 解耦:横切逻辑与业务分离。
  • 可复用:切面可跨模块使用。
  • 非侵入:目标代码无需修改。

缺点

  • 限制:仅支持方法级别。
  • 复杂性:调试代理逻辑困难。
  • 性能:代理有微小开销。

8. 延伸与面试角度

  • 与单点登录
  • AOP 可拦截请求,验证 JWT。
  • 例:
@Before("execution(* com.example.controller.*.*(..))")
public void checkToken() {
    // 验证 Authorization 头
}
  • 与线程安全
  • AOP 可封装锁逻辑。
  • 例:用 @AroundReentrantLock
  • 实际应用
  • 电商:日志、事务、权限。
  • 微服务:统一异常处理。
  • 面试点
  • 问“原理”时,提代理和织入。
  • 问“场景”时,提示例代码。

9. 总结

Spring AOP 通过代理模式实现面向切面编程,支持方法级别的 Before、After、Around 等通知,适用于日志、事务、权限等场景。基于 JDK 动态代理或 CGLIB,运行时织入,简单但功能有限。面试时,可提概念、代码或与 AspectJ 对比,结合单点登录等场景,展示理解深度。


如果您有具体场景或代码问题,请提供更多细节,我可以进一步优化答案!