aop是面向切面编程,用于解耦横切关注点;它非oop并列或升级,而是对其在日志、事务等场景的缝合补充。

什么是AOP,它和OOP不是“并列关系”
Java 中的 AOP 不是 OOP 的补充或升级,而是对 OOP 在特定问题上的“缝合手段”。OOP 擅长建模实体及其行为(比如 User 有 login()),但像日志、事务、权限校验这类横跨多个类的逻辑,硬塞进每个方法里会破坏单一职责,也难复用。AOP 就是把这类“横切关注点”抽出来,不侵入业务代码地织入执行流程。
Spring AOP 为什么只支持方法级别的增强
Spring AOP 底层用的是动态代理(JDK Proxy 或 CGLIB),本质是在运行时生成目标类的代理对象,在调用前后插入逻辑。这意味着它只能拦截 public 方法调用——private 方法、static 方法、final 方法、构造器、字段访问都不行。这不是设计缺陷,而是代理机制的天然限制。
常见错误现象:IllegalArgumentException: Not a managed type 或增强没生效,往往是因为目标方法不是 public,或者调用发生在同一个类内部(this.login() 走的是原对象,不经过代理)。
- 确保被增强的方法是 public 的
- 避免在同一个类里 self-invocation(比如 service 内部调用另一个加了
@Transactional的方法) - 需要更细粒度控制(如字段修改、异常抛出点)得用 AspectJ 编译期织入,而非 Spring AOP
@Around 和 @Before/@After 的关键区别在哪
@Around 是唯一能控制是否执行原方法、修改参数、替换返回值、捕获异常的增强类型;@Before 和 @After 只能“旁观”,不能干预流程。
立即学习“Java免费学习笔记(深入)”;
使用场景:事务管理必须用 @Around,因为要在方法执行前开启事务、执行后提交或回滚;而单纯打日志,@Before + @AfterReturning 就够了,更轻量。
性能影响:所有增强都会带来反射调用和代理开销,@Around 因为包裹整个方法执行,堆栈更深、对象创建更多。高频调用的方法上慎用复杂逻辑的 @Around。
-
@Around的参数必须是ProceedingJoinPoint,且必须显式调用proceed() - 忘记调用
proceed()会导致目标方法完全不执行,且无报错提示 -
@AfterThrowing的异常参数名要和注解中throwing = "e"一致,否则绑定失败
切点表达式写错最常卡在哪几个地方
切点(pointcut)是 AOP 的命门,写错就等于没配。最典型的三类错误:包路径通配符误用、方法签名匹配不全、访问修饰符遗漏。
常见错误现象:execution(* com.example.service..*.*(..)) 看似覆盖所有 service 方法,但如果实际类在 com.example.service.impl,而你漏写了 impl 下的子包,就匹配不到;又或者写成 * com.example.service.*Service.*(..),却忘了 service 类名是 UserServiceImpl 而非 UserService。
- 包路径末尾用
..表示任意深度子包,用.*只表示当前层 - 方法参数写
(..)匹配任意参数个数和类型,()只匹配无参 - 默认只匹配 public 方法,要匹配 protected 或 package-private,得显式写出访问修饰符,如
execution(protected * *(..))
真正容易被忽略的是:切点表达式里的类名必须是运行时实际加载的类(比如 CGLIB 生成的 UserServiceImpl$$EnhancerBySpringCGLIB$$...),但 Spring AOP 切点默认按原始类名匹配,所以一般不用管代理类名——除非你手动 new 出对象,绕过了 Spring 容器。









