这两者经常被混淆,很多人误以为 Spring AOP 就是 AspectJ 的实现。实际上它们是两个不同的 AOP 框架,有着本质区别。
一、核心区别总览
| 对比维度 | Spring AOP | AspectJ |
|---|---|---|
| 本质 | 基于代理的 AOP 实现 | 基于字节码织入的 AOP 实现 |
| 实现机制 | JDK 动态代理 / CGLIB | 编译期/类加载期字节码修改 |
| 织入时机 | 运行时 | 编译期、类加载期、运行期 |
| 连接点粒度 | 仅方法级别 | 方法、字段、构造器、静态初始化等 |
| 性能 | 有代理调用开销 | 直接执行增强代码,性能更好 |
| 是否需要 Spring 容器 | ✅ 必须 | ❌ 不必须 |
| 增强 private 方法 | ❌ 不支持 | ✅ 支持 |
| 增强 final 类/方法 | ❌ 不支持(CGLIB 限制) | ✅ 支持 |
| IDE 支持 | 通用 | Eclipse AJDT、IntelliJ 插件 |
| 学习曲线 | 简单 | 较陡峭 |
二、实现原理深度对比
Spring AOP 原理
java
// Spring AOP 实际是代理模式
public class UserServiceProxy extends UserService {
private UserService target;
@Override
public void saveUser(User user) {
// 前置通知
System.out.println("Before save");
// 调用目标方法
target.saveUser(user);
// 后置通知
System.out.println("After save");
}
}
AspectJ 原理
java
// 原始代码
public class UserService {
public void saveUser(User user) {
System.out.println("Saving user");
}
}
// AspectJ 编译后实际生成的字节码等价于
public class UserService {
public void saveUser(User user) {
// 通知代码直接织入
System.out.println("Before save");
System.out.println("Saving user");
System.out.println("After save");
}
}
三、功能范围对比
连接点支持度
| 连接点类型 | Spring AOP | AspectJ | 示例场景 |
|---|---|---|---|
| 方法调用 | ✅ | ✅ | 最常用 |
| 方法执行 | ✅ | ✅ | 区别在于代理与目标 |
| 构造器调用 | ❌ | ✅ | 对象创建监控 |
| 字段读取/设置 | ❌ | ✅ | 属性变更校验 |
| 静态初始化 | ❌ | ✅ | 类加载时操作 |
| 异常处理 | ❌ | ✅ | 异常处理器增强 |
| 对象初始化 | ❌ | ✅ | 构造后初始化逻辑 |
| 注解类型匹配 | ✅ | ✅ | 基于注解的增强 |
AspectJ 独有能力示例
java
@Aspect
public class AspectJUniqueCapabilities {
// 1. 拦截字段赋值
@Before("set(* com.example.User.name)")
public void beforeSetName(JoinPoint jp) {
String newName = (String) jp.getArgs()[0];
if (newName == null || newName.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
}
// 2. 拦截字段读取
@Before("get(* com.example.User.password)")
public void beforeGetPassword() {
System.out.println("正在读取密码字段");
}
// 3. 拦截构造器
@Before("new(com.example.User)")
public void beforeConstructor() {
System.out.println("User 对象即将被创建");
}
// 4. 拦截静态初始化块
@Before("staticinitialization(com.example.User)")
public void beforeStaticInit() {
System.out.println("User 类正在静态初始化");
}
// 5. 拦截异常处理器
@Before("handler(Exception+)")
public void beforeExceptionHandler() {
System.out.println("即将处理异常");
}
// 6. 为类添加新方法(引入)
@DeclareParents(value = "com.example.User+",
defaultImpl = AuditableImpl.class)
public static Auditable mixin;
}
四、性能对比测试
java
// 性能测试示例
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class AOPPerformanceTest {
@Benchmark
public void springAOP() {
userService.saveUser(new User()); // Spring AOP 代理
}
@Benchmark
public void aspectJ() {
userService.saveUser(new User()); // AspectJ 织入
}
// 典型结果(基于 1000 万次调用):
// Spring AOP: ~450ns/op(有代理开销)
// AspectJ: ~120ns/op(几乎无额外开销)
}
五、实战选择指南
使用 Spring AOP 的场景
java
// ✅ 适合 Spring AOP 的场景
@Service
public class OrderService {
// 1. 声明式事务(经典场景)
@Transactional
public void createOrder(Order order) { }
// 2. 方法级缓存
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) { }
// 3. 权限检查
@PreAuthorize("hasRole('ADMIN')")
public void deleteOrder(Long id) { }
// 4. 方法执行时间监控
@Around("@annotation(com.example.Monitored)")
public Object monitor(ProceedingJoinPoint pjp) { }
}
必须使用 AspectJ 的场景
java
// ✅ 必须用 AspectJ 的场景
@Aspect
public class MustUseAspectJ {
// 1. 拦截 private 方法
private void internalLogic() { } // Spring AOP 无法增强
// 2. 拦截 static 方法
public static void staticMethod() { }
// 3. 拦截 final 方法
public final void finalMethod() { }
// 4. 拦截字段访问(领域驱动设计中的属性校验)
private String email;
// 5. 不需要 Spring 容器的独立应用
// 6. 对性能要求极高的场景(如每秒数万次调用)
// 7. 需要拦截构造器(如单例模式强制校验)
}
六、配置对比
Spring AOP 配置
java
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringAOPConfig {
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}
// application.properties
spring.aop.auto=true
spring.aop.proxy-target-class=true
AspectJ LTW 配置
java
// JVM 启动参数
// -javaagent:path/to/aspectjweaver-1.9.19.jar
// META-INF/aop.xml
<aspectj>
<weaver options="-verbose">
<include within="com.example..*"/>
</weaver>
<aspects>
<aspect name="com.example.aspect.PerformanceAspect"/>
</aspects>
</aspectj>
// Spring Boot 启用 LTW
@Configuration
@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
public class LTWConfig {
}
七、常见陷阱与解决方案
Spring AOP 陷阱
java
@Service
public class UserService {
// 陷阱1:内部方法调用不生效
@Transactional
public void outer() {
this.inner(); // ❌ 事务不生效(直接调用,未通过代理)
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() { }
// 解决方案
public void fixed() {
((UserService) AopContext.currentProxy()).inner(); // ✅ 通过代理调用
// 或注入自身:@Autowired private UserService self;
}
// 陷阱2:public 方法限制
@Transactional
private void privateMethod() { } // ❌ 事务不生效
// 陷阱3:final 方法
@Transactional
public final void finalMethod() { } // ❌ CGLIB 无法代理 final 方法
}
AspectJ 陷阱
XML
// 陷阱:IDE 编译不支持 AspectJ 语法
// 解决:使用 ajc 编译器或配置 Maven/Gradle 插件
// Maven 配置
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>11</complianceLevel>
<source>11</source>
<target>11</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
八、混合使用策略
java
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // Spring AOP
@EnableLoadTimeWeaving(aspectjWeaving = ENABLED) // AspectJ LTW
public class HybridConfig {
// 简单场景用 Spring AOP
@Bean
public LogAspect logAspect() { return new LogAspect(); }
// 复杂场景(字段拦截)用 AspectJ
// 通过 META-INF/aop.xml 配置
}
九、选择决策树
bash
是否需要拦截 private/static/final 方法或字段?
├─ 是 → 使用 AspectJ
└─ 否 → 继续
是否需要拦截构造器或静态初始化块?
├─ 是 → 使用 AspectJ
└─ 否 → 继续
是否需要高频率调用(>10万次/秒)?
├─ 是 → 使用 AspectJ
└─ 否 → 继续
是否完全基于 Spring 框架?
├─ 是 → Spring AOP(更简单、易调试)
└─ 否 → AspectJ(独立使用)
是否关注编译/启动速度?
├─ 是 → Spring AOP(无额外织入开销)
└─ 否 → AspectJ(首次启动稍慢,但运行更快)
十、总结
| 方面 | Spring AOP | AspectJ |
|---|---|---|
| 推荐度(Spring 项目) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 推荐度(非 Spring 项目) | ⭐ | ⭐⭐⭐⭐⭐ |
| 学习成本 | 低 | 中高 |
| 调试难度 | 低(可见代理类) | 高(字节码层面) |
| 社区支持 | 极好 | 良好 |
最佳实践:
-
80% 的场景用 Spring AOP 就够了
-
只有在遇到 Spring AOP 的限制时,才考虑迁移到 AspectJ
-
可以混合使用:常用功能用 Spring AOP,特殊需求用 AspectJ LTW