这两者经常被混淆,很多人误以为 spring aop 就是 aspectj 的实现。实际上它们是两个不同的 aop 框架,有着本质区别。
一、核心区别总览
| 对比维度 | spring aop | aspectj |
|---|---|---|
| 本质 | 基于代理的 aop 实现 | 基于字节码织入的 aop 实现 |
| 实现机制 | jdk 动态代理 / cglib | 编译期/类加载期字节码修改 |
| 织入时机 | 运行时 | 编译期、类加载期、运行期 |
| 连接点粒度 | 仅方法级别 | 方法、字段、构造器、静态初始化等 |
| 性能 | 有代理调用开销 | 直接执行增强代码,性能更好 |
| 是否需要 spring 容器 | ✅ 必须 | ❌ 不必须 |
| 增强 private 方法 | ❌ 不支持 | ✅ 支持 |
| 增强 final 类/方法 | ❌ 不支持(cglib 限制) | ✅ 支持 |
| ide 支持 | 通用 | eclipse ajdt、intellij 插件 |
| 学习曲线 | 简单 | 较陡峭 |
二、实现原理深度对比
spring aop 原理
// 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 原理
// 原始代码
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 独有能力示例
@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;
}四、性能对比测试
// 性能测试示例
@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 的场景
// ✅ 适合 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 的场景
// ✅ 必须用 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 配置
@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=trueaspectj ltw 配置
// 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 陷阱
@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 陷阱
// 陷阱: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>八、混合使用策略
@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 配置
}九、选择决策树
是否需要拦截 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
到此这篇关于spring aop 与 aspectj的使用小结的文章就介绍到这了,更多相关spring aop 与 aspectj内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论