Spring AOP 与 AspectJ 的关系
结论先行:两者不是"一个框架的两个名字"——AspectJ 是独立完整的 AOP 框架(语言级扩展,编译期/类加载期织入字节码,支持字段、构造器、静态初始化等更多连接点,可以切非 Spring 管理的任何 Java 类);Spring AOP 只是 Spring 容器内的代理式 AOP 子集,它借用了 AspectJ 的注解与切点表达式语法(@Aspect/@Pointcut/execution),但底层仍是运行期 JDK/CGLIB 动态代理,只作用于容器 Bean 的 public 方法执行。
一、对比表
| 对比项 | Spring AOP | AspectJ |
|---|---|---|
| 织入方式 | 运行期动态代理(生成代理对象) | 编译期织入 / 加载期织入(LTW,改字节码) |
| 连接点范围 | 仅 Bean 的 public 方法执行 | 方法、字段读写、构造器、static 初始化、异常处理等 |
| 可切对象 | 仅 Spring 容器管理的 Bean | 任意 Java 类(含第三方 jar 内类) |
| 依赖 | spring-aop / spring-aspects | aspectjweaver、aspectjrt(或编译器插件) |
| 使用难度 | 低,工程主流 | 高,配置重 |
| 适用 | 事务、缓存、日志、权限等业务切面 | 需要织入非 Spring 对象、字段级或静态资源切面 |
二、代码(注意语法来源)
@Aspect // 注解语法来自 AspectJ,但运行时由 Spring AOP 代理实现
@Component
public class MetricsAspect {
@Around("@annotation(com.demo.annotation.Metrics)") // 切点表达式也是 AspectJ 语法
public Object around(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();
}
}
// 结论:@EnableAspectJAutoProxy 只是开启 Spring 自己的代理切面,并不会真的启动 AspectJ
三、选型建议
- 事务/缓存/安全/日志等常规横切关注点 → Spring AOP 足够,零额外依赖、与 IoC 无缝整合;
- 需要织入第三方/非 Spring 管理的类、字段级或构造器切面 → 再引入 AspectJ(LTW 或编译期织入);
- 已有 AspectJ 基建、追求极致织入能力的团队可全程 AspectJ,但绝大多数业务系统没有必要。
常见追问 / 记忆点
- 追问 1:什么时候才需要真 AspectJ?答:要织入非 Spring 管理的类、要对字段/构造器做切面、或目标类 final 无法被代理时,再考虑 AspectJ 加载期织入(-javaagent:aspectjweaver 或 aop.xml)。
- 追问 2:Spring Boot 需要手动开 AOP 吗?答:引入 spring-boot-starter-aop 后自动配置生效,无需 @EnableAspectJAutoProxy(它已内置)。
- 追问 3:为什么 Spring 不直接用 AspectJ?答:代理方案零学习成本、与 IoC 无缝整合,覆盖 95% 的企业切面需求,代价是牺牲一部分连接点能力。
- 记忆点:"Spring AOP 是代理,AspectJ 是织入;Spring 只借了 AspectJ 的语法,没借它的运行时"。