Spring AOP和AspectJ比较

@wanqiuz 2018-05-19 16:08:24发表于 wanqiuz/blog-articles AOPAspectJSpring

1. 介绍

如今,有很多AOP库,使用之前首先要回答下面的问题

  • 它和现有的应用和新的应用兼容吗
  • 可以在哪里实现AOP
  • 它和现有应用集成有多快
  • 性能开销多少
    下面将介绍目前最流行的两种Java AOP框架:Spring AOP和AspectJ。

2. AOP概念

在开始之前,首先快速回顾一下术语和核心概念:

  • Aspect--切面:分散在应用程序多个位置的代码/特性,通常与实际的业务逻辑(例如事务管理)不同,每个切面侧重于特定的切向功能。
  • Joinpoint--连接点:在执行诸如方法执行、构造函数调用或字段赋值等程序时一个特定的连接点。
  • Advice--增强:在特定连接点上的切面采取的行动
  • Pointcut--切点:用来匹配连接点的正则表达式,每当一个连接点匹配一个切点时,就会执行与该切点相关的指定增强。
  • Weaving--编制:将切面和目标对象连接并创造出一个增强了的对象的过程

3. Spring AOP和AspectJ

3.1. 兼容性和目标

简单地说,Spring AOP和AspectJ有不同的目标。
Spring AOP旨在通过Spring IoC提供一个简单的AOP实现,以解决程序员所面临的最常见的问题。它不是一个完整的AOP解决方案——它只能应用于由Spring容器管理的bean。
另一方面,AspectJ是最初的AOP技术,旨在提供完整的AOP解决方案。它更健壮,但也比Spring AOP更复杂。同样值得注意的是,AspectJ可以应用于所有领域对象。

3.2. 编织

AspectJ和Spring AOP都使用不同类型的编织,这影响了它们的性能和易用性。
AspectJ使用了三种不同类型的编织:

  • Compile-time weaving--编译时编织:AspectJ编译器将切面的源代码和应用程序作为输入,生成一个编织类文件作为输出。
  • Post-compile weaving--编译后的编织:这也称为二进制编织。它用于将现有的类文件和JAR文件与切面编织起来。
  • Load-time weaving--加载时编织:它与前一个二进制编织类似,不同的是,编织被延迟,直到类加载器将类文件加载到JVM。
    有关AspectJ本身的更深入的信息,请访问本文
    AspectJ使用编译时和类加载时间编织,Spring AOP利用运行时编织。
    使用运行时编织,在应用程序执行过程中使用目标对象的代理(使用JDK动态代理或CGLIB代理(在下一个点讨论):
    springaop-process

3.3. 内部结构及应用

Spring AOP是一个基于代理的AOP框架。这意味着要它将创建该对象的代理,从而对目标对象实现切面。这是通过两种方式实现的:

  • JDK动态代理——Spring AOP的首选方法。只要目标对象实现了一个接口,那么就会使用JDK动态代理。
  • CGLIB代理——如果目标对象没有实现接口,那么可以使用CGLIB代理。
    我们可以从官方文档中了解更多关于Spring AOP代理机制的知识。
    另一方面,AspectJ在运行时不做任何事情,因为类是直接和切面一起编译的。
    与Spring AOP不同,它不需要任何设计模式。为了将这些切面编织到代码中,它引入了名为AspectJ compiler(ajc)的编译器,通过它我们编译程序,然后通过提供一个小的(< 100K)运行时库来运行它。

3.4. 连接点

在第3.3节中,我们展示了基于代理模式Spring AOP。因此,它需要子类化目标Java类并相应地应用横切关注点。
但它也有局限性。我们不能对final类应用横切关注点(或切面),因为它们不能被覆盖,会导致运行时异常。
静态和final方法同样适用。Spring切面不能应用于它们,因为它们不能被覆盖。因此,由于这些限制,Spring AOP只支持方法执行的连接点。
然而,AspectJ在运行前就将横切关注点直接插入到实际代码中。与Spring AOP不同,它不需要子类化目标对象,因此也支持许多其他的连接点。以下是支持的连接点摘要:

Joinpoint Spring AOP Supported AspectJ Supported
Method Call No Yes
Method Execution Yes Yes
Constructor Call No Yes
Constructor Execution No Yes
Static initializer execution No Yes
Object initialization No Yes
Field reference No Yes
Field assignment No Yes
Handler execution No Yes
Advice execution No Yes

同样值得注意的是,在Spring AOP中,切面并不应用于同一类中调用的方法。
这显然是因为当我们在同一个类中调用一个方法时,我们不会调用Spring AOP提供的代理的方法。如果我们需要这个功能,那么我们必须在不同的bean中定义一个独立的方法,或者使用AspectJ。

3.5. 简单

Spring AOP显然更简单,因为它不会在我们的构建过程之间引入任何额外的编译器或编织器。它使用运行时编织,因此它与我们通常的构建过程无缝集成。尽管它看起来很简单,但是它只处理由Spring管理的bean。
但是,要使用AspectJ,我们需要引入AspectJ compiler(ajc)并重新打包所有的库(除非我们切换到post-compile或者load-time weaving)。
当然,这比前者更复杂,因为它引入了AspectJ Java工具(包括一个编译器(ajc)、一个调试器(ajdb)、一个文档生成器(ajdoc)、一个程序结构浏览器(ajbrowser)),我们需要将其与IDE或构建工具集成在一起。

3.6. 性能

就性能而言,编译时编织比运行时编织要快得多。Spring AOP是一个基于代理的框架,所以在应用程序启动时需要创建代理。此外,每个切面还有一些方法调用,这会对性能产生负面影响。
另一方面,AspectJ在应用程序执行之前将切面的内容整合到主代码中,因此和Spring AOP不同,AspectJ没有额外的运行时开销。
基于这些原因,基准测试表明AspectJ几乎比Spring AOP快8到35倍。

4. 总结

下面这个表总结了Spring AOP和AspectJ之间的关键区别:

Spring AOP AspectJ
Implemented in pure Java Implemented using extensions of Java programming language
No need for separate compilation process Needs AspectJ compiler (ajc) unless LTW is set up
Only runtime weaving is available Runtime weaving is not available. Supports compile-time, post-compile, and load-time Weaving
Less Powerful – only supports method level weaving More Powerful – can weave fields, methods, constructors, static initializers, final class/methods, etc…
Can only be implemented on beans managed by Spring container Can be implemented on all domain objects
Supports only method execution pointcuts Support all pointcuts
Proxies are created of targeted objects, and aspects are applied on these proxies Aspects are weaved directly into code before application is executed (before runtime)
Much slower than AspectJ Better Performance
Easy to learn and apply Comparatively more complicated than Spring AOP

5. 选择正确的框架

如果我们分析这一节中所有的论点,我们就会开始明白,不能绝对地说一个框架比另一个更好。
简单地说,选择很大程度上取决于我们的需求:

  • 框架:如果应用程序没有使用Spring框架,那么我们别无选择,只能放弃使用Spring AOP的想法,因为它无法管理Spring容器之外的任何东西。但是,如果我们的应用程序完全是使用Spring框架创建的,那么我们可以使用Spring AOP,因为它很容易学习和应用。
  • 灵活性:考虑到有限的连接点支持,Spring AOP并不是一个完整的AOP解决方案,但是它解决了程序员所面临的最常见的问题。尽管如果我们想深入挖掘并利用AOP的最大能力,并希望得到广泛的可用连接点的支持,那么AspectJ就是选择。
  • 性能: 如果我们使用的是有限的切面,那么就会有细微的性能差异。但是有时候应用程序有成千上万个切面。我们不希望在这种情况下使用运行时编织,所以最好选择AspectJ。AspectJ的速度比Spring AOP快8到35倍。
  • 最好的两个:这两个框架是完全兼容的。只要有可能,我们总是可以利用Spring AOP,并且仍然使用AspectJ来获得不受前者支持的连接点的支持。
  1. 结论
    在本文中,我们在几个关键领域分析了Spring AOP和AspectJ。
    我们比较了两种方法对AOP的灵活性以及它们与我们的应用程序的适应程度。

参考并感谢: