搞懂 AOP:核心概念 + 实战案例 + 事务失效终极排查指南

举报
柠檬味拥抱 发表于 2025/04/24 09:42:41 2025/04/24
【摘要】 🧩 一、基础理解类 1. 什么是 AOP?答: AOP(Aspect-Oriented Programming)即面向切面编程,是一种通过预编译方式和运行期动态代理实现程序功能统一维护的技术。它可以用来增强代码,比如日志记录、权限校验、事务处理等。 2. AOP 的主要作用?答: 将横切关注点(如日志、权限、事务等)从业务逻辑中分离,使得代码更清晰、可维护、可复用。 3. AOP 的常见...

🧩 一、基础理解类

1. 什么是 AOP?

答: AOP(Aspect-Oriented Programming)即面向切面编程,是一种通过预编译方式和运行期动态代理实现程序功能统一维护的技术。它可以用来增强代码,比如日志记录、权限校验、事务处理等。


2. AOP 的主要作用?

答: 将横切关注点(如日志、权限、事务等)从业务逻辑中分离,使得代码更清晰、可维护、可复用。


3. AOP 的常见应用场景?

答:

  • 日志记录
  • 权限控制
  • 事务管理
  • 参数校验
  • 缓存处理

🧩 二、核心概念类

4. AOP 中的几个核心术语?

术语 含义
Join Point 程序执行的某个点,如方法调用、异常抛出
Pointcut 表达式,用于匹配 Join Point
Advice 要织入到 Join Point 的操作(前置/后置/环绕等)
Aspect 切面,是 Pointcut 和 Advice 的组合
Weaving 把切面织入目标对象的过程(编译期/加载期/运行期)
Target Object 被增强的对象(业务逻辑类)

5. Spring AOP 支持哪些类型的 Advice?

  • @Before:方法执行前执行
  • @After:方法执行后执行(无论是否异常)
  • @AfterReturning:方法成功返回后执行
  • @AfterThrowing:方法抛出异常后执行
  • @Around:环绕通知,最强大

6. 如何定义一个切面(Aspect)?

@Aspect
@Component
public class LogAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("调用前:" + joinPoint.getSignature());
    }
}

🧩 三、实现机制类

7. Spring AOP 是如何实现的?

答: Spring AOP 基于 动态代理实现

  • 如果目标类实现了接口,使用 JDK 动态代理;
  • 如果没有实现接口,使用 CGLIB 字节码生成。

8. Spring AOP 和 AspectJ 的区别?

对比项 Spring AOP AspectJ
实现方式 运行时代理 编译期/加载期字节码织入
支持类型 方法级别 更强(包括字段、构造器等)
使用复杂度 简单,适合大多数场景 复杂,功能更强大

9. Pointcut 表达式怎么写?

execution([访问修饰符] 返回类型 [包名].类名.方法名(参数))

示例:

execution(* com.example.service.UserService.*(..))

匹配 UserService 类中所有方法。


🧩 四、拓展进阶类

10. @Around 通知如何控制原方法执行?

@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("方法前");
    Object result = pjp.proceed(); // 执行原方法
    System.out.println("方法后");
    return result;
}

11. AOP 能否对 private 方法进行增强?

答: Spring AOP(基于代理)无法增强 private 方法,因为代理类无法访问目标类的 private 方法。如果需要增强 private 方法,需要用到 AspectJ 的编译期织入(Compile-Time Weaving)


12. AOP 与 IOC 有什么关系?

答:

  • IOC 负责对象的创建和依赖注入;
  • AOP 是对 IOC 管理的 Bean 进行功能增强; Spring 通过 IOC 管理 AOP 代理对象,从而实现切面逻辑的织入。

✅ 五、实战中常见问题

13. AOP 对 Bean 生命周期有什么影响?

答: AOP 增强的对象是代理对象,不影响 Spring 的生命周期管理,但你注入的是代理而不是原始对象。


14. 为什么 @Transactional 失效?

可能原因:

  • 方法是 private
  • 方法是 final
  • 当前方法调用的是本类中另一个加了 @Transactional 的方法(即“自调用”);
  • 没有开启 @EnableTransactionManagement
  • 抛出的异常不是 RuntimeException

image-20250423160110620

事务(@Transactional)是 Spring 框架中非常核心的功能之一,但在实际开发中经常遇到事务失效的问题。下面我为你整理了 事务失效的常见场景,并解释原因与解决方案,方便你在面试或项目中快速定位问题👇


✅ 一、事务失效的常见场景及原因

1️⃣ 方法不是 public

  • Spring AOP 默认只支持 public 方法的事务代理。
  • 失效原因:非 public 方法不会被代理,因此 @Transactional 不生效。
@Transactional
private void doSomething() {} // ❌ 事务无效

解决:将方法改为 public


2️⃣ 方法是 final 修饰的

  • JDK 动态代理 & CGLIB 都不能对 final 方法进行增强。
  • 失效原因:无法生成代理类,事务无法被织入。
@Transactional
public final void saveData() {} // ❌ 无效

解决:去掉 final 修饰符。


3️⃣ 自调用(同类内部调用)

  • 当前类中的一个方法调用另一个加了事务的方法,Spring AOP 无法拦截自调用。
  • 失效原因:Spring 的事务是基于代理的,自己调用自己不会走代理对象。
public class UserService {
    public void outer() {
        inner(); // ❌ 调用不经过代理,事务失效
    }

    @Transactional
    public void inner() {
        // 期望事务的代码
    }
}

解决方案

  • 把被调用方法提取到其他 @Component 类中;
  • 或使用 AopContext.currentProxy() 调用代理对象。
((UserService) AopContext.currentProxy()).inner(); // ✅ 有效

4️⃣ 异常被捕获处理了

  • Spring 默认只对 运行时异常(RuntimeException) 进行事务回滚;
  • 如果异常被 try-catch 捕获并吞掉,事务就不会回滚。
@Transactional
public void update() {
    try {
        // 异常代码
    } catch (Exception e) {
        // 事务不会回滚 ❌
    }
}

解决

  • 把异常重新抛出;
  • 或设置 rollbackFor:
@Transactional(rollbackFor = Exception.class)

5️⃣ 抛出的是受检异常(Checked Exception)

  • 默认只在抛出 RuntimeException 时才会回滚。
@Transactional
public void update() throws IOException {
    throw new IOException(); // ❌ 不会回滚
}

解决

@Transactional(rollbackFor = IOException.class) // ✅ 指定异常回滚

6️⃣ 事务注解加在接口或接口方法上

  • Spring AOP 默认只拦截实现类的方法。
public interface UserService {
    @Transactional
    void save(); // ❌ 无效
}

解决:应将 @Transactional 加在实现类上。


7️⃣ 没有开启事务注解支持

  • 忘记加 @EnableTransactionManagement
@Configuration
@EnableTransactionManagement // ✅ 必须有
public class AppConfig {}

8️⃣ 数据库引擎不支持事务

  • 如 MySQL 的 MyISAM 引擎不支持事务,只有 InnoDB 才支持。
  • 解决:确认数据库表引擎是否为 InnoDB

✅ 二、事务失效排查建议

  1. 检查 @Transactional 加在哪个类/方法;
  2. 方法是否 public 且未 final
  3. 是否发生了 自调用
  4. 是否开启 @EnableTransactionManagement
  5. 异常是否被捕获、是否为受检异常;
  6. 使用断点调试或日志验证是否被代理;
  7. 检查数据库是否支持事务。

image-20250423164447261

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。