Spring AOP和AspectJ的区别

一、实现原理的本质区别

Spring AOP是基于动态代理实现的。在运行时,Spring会为目标Bean创建一个代理对象。如果目标类实现了接口,就用JDK动态代理;如果没有接口,就用CGLIB生成子类代理。所有方法调用都先经过代理对象,代理对象负责执行切面逻辑,然后再调用真实对象的方法。AspectJ则是基于字节码织入的。它直接修改目标类的字节码,把切面逻辑织入到类中。这个织入可以发生在编译时、编译后或类加载时。织入完成后,切面代码就已经是类的一部分了,不需要代理对象。

二、织入时机

Spring AOP只在运行时织入。当Spring容器启动,创建Bean时才生成代理对象。这个过程完全是在JVM运行期间完成的。AspectJ支持三种织入时机:
  • 编译时织入:使用AspectJ编译器替代javac,在编译源码时就把切面织入
  • 编译后织入:对已编译的class文件或jar包进行织入,适合第三方库
  • 加载时织入:通过Java Agent在类加载到JVM时进行织入

三、功能强度差异(重要)

这是两者最大的区别。Spring AOP的局限性:
  • 只能拦截方法执行,不能拦截字段访问、构造器等
  • 只能代理Spring Bean,普通Java对象无法代理
  • 只支持public方法,private、protected、final、static方法都不行
  • 无法处理内部调用:同一个类内部方法互相调用时,切面不生效,因为没走代理
我在实际项目中就遇到过这个坑:一个Service类的methodA调用了methodB,两个方法都加了@Transactional,结果methodB的事务没生效,就是因为内部调用绕过了代理。AspectJ的完整功能:
  • 支持11种连接点:方法执行、方法调用、字段读写、构造器、静态初始化等等
  • 可以拦截任何Java对象,不限于Spring Bean
  • 支持所有修饰符的方法:public、private、static、final都可以
  • 内部调用也能拦截,因为切面代码已经织入到字节码里了
  • 可以拦截第三方库的代码,比如监控JDBC连接池

四、性能差异

Spring AOP有一定的性能开销。每次方法调用都要经过代理层,涉及反射、方法拦截链的遍历等。在高并发场景下,这个开销会被放大。AspectJ几乎没有运行时开销。因为切面逻辑在编译或加载时就已经织入到字节码了,运行时就是直接执行,跟手写代码的性能基本一致。不过对于大多数企业应用来说,Spring AOP的性能完全够用。除非是高频交易系统或者对性能极其敏感的场景,才需要考虑AspectJ。

五、使用复杂度

Spring AOP非常简单:
  • 加个依赖spring-boot-starter-aop
  • 启用@EnableAspectJAutoProxy
  • 写个@Aspect类,用@Before、@After等注解
  • 完全不需要特殊配置
AspectJ就复杂多了:
  • 需要引入AspectJ的依赖和编译器
  • 编译时织入需要用ajc编译器替代javac
  • 加载时织入需要配置Java Agent参数
  • 还要配置aop.xml文件
  • 学习成本和维护成本都比较高

六、实际项目中的选择

根据我的实践经验:选择Spring AOP的场景(占90%):
  • 普通Spring Boot应用
  • 拦截Service层的方法调用
  • 实现日志、权限校验、性能监控、事务管理等
  • 团队成员对AOP不太熟悉,需要快速上手
选择AspectJ的场景(占10%):
  • 需要拦截private方法或字段访问
  • 需要解决内部调用的问题
  • 要对第三方库进行增强,无法修改源码
  • 对性能要求极高的场景
  • 开发通用框架或中间件,需要更强大的AOP能力

七、一个混合使用的说明

现在Spring AOP其实已经整合了AspectJ。我们平时用的@Aspect、@Before、@Pointcut这些注解,以及execution、within这些切点表达式,都是AspectJ的语法。但底层实现还是Spring的动态代理。这叫"使用AspectJ的语法,但由Spring AOP执行"。如果想真正用AspectJ的织入能力,需要在配置里启用@EnableLoadTimeWeaving,并配置相应的Java Agent。

八、错误场景

我可以再补充一个常见的错误场景:很多人以为加了@Async或@Transactional就能生效,结果发现不work。原因就是:
  • 同类内部调用,走的是this.method(),不是代理对象
  • 方法是private的,代理拦截不到
  • 或者类没有被Spring管理
这些都是Spring AOP代理机制的局限性。如果真要解决,要么重构代码(把方法提取到另一个Bean),要么就得用AspectJ的编译时织入。

总结

简单来说:
  • Spring AOP:基于代理、运行时织入、功能够用、简单易上手,适合90%的场景
  • AspectJ:基于字节码、编译/加载时织入、功能强大、配置复杂,适合特殊场景

 
posted @ 2026-01-25 19:51  菜鸟~风  阅读(0)  评论(0)    收藏  举报