Android AOP方案对比
什么是面向切面编程(Aop)
面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它允许开发者通过切面(Aspect)来模块化横切关注点,并将它们封装成可重用的组件。横切关注点是指那些存在于应用程序中多个模块中的功能,它们通常与应用程序的核心业务逻辑无关,例如日志记录、事务管理、安全性等。
在面向切面编程中,切面是一个模块化的单元,它横跨一个或多个对象,提供了在应用程序中的多个位置进行代码重用的机制。切面可以定义一组横切关注点,并将它们与应用程序的核心业务逻辑分离开来。这样,当需要修改或者添加新的横切关注点时,只需要修改或者添加对应的切面,而不需要修改核心业务逻辑代码。
面向切面编程的核心概念是切面、连接点、通知、切点和增强。其中,切面是关注点的模块化单元,连接点是在应用程序中可以插入切面的点,通知是切面在连接点处执行的代码,切点是一组连接点的集合,而增强是在切点处执行的通知的行为。
总的来说,面向切面编程通过将横切关注点与核心业务逻辑进行解耦,提高了代码的模块化性、可维护性和可重用性,使得应用程序更易于理解、扩展和维护。
主流AOP技术分类
首先,从织入的时机的角度看,可以分为源码阶段、class阶段、dex阶段、运行时织入。
对于前三项源码阶段、class阶段、dex织入,由于他们都发生在class加载到虚拟机前,我们统称为静态织入, 而在运行阶段发生的改动,我们统称为动态织入。
静态织入:
- APT
- AspectJ
- ASM
- Javassit
动态织入:
- java动态代理
- cglib
- Javassit
静态织入发生在编译器,因此几乎不会对运行时的效率产生影响;动态织入发生在运行期,可直接将字节码写入内存,并通过反射完成类的加载,所以效率相对较低,但更灵活。
其次,我们需要关注这些框架具备哪切面编程的能力,这有助于帮助我做技术选型,由于AspectJ、ASM 、Javassit是相对比较完善的AOP框架,因此只对三者进行比较。
| 能力 | AspectJ | ASM | Javassit |
|---|---|---|---|
| 切面抽象 | ✅ | ||
| 切点抽象 | ✅ | ||
| 通知类型抽象 | ✅ | ✅ | ✅ |
其中:
- 切面抽象:具备筛选过滤class的能力,比如我们想为Activity的所有生命周期织入代码,那你是不是首先需要具备过滤Activity及其子类的能力。
- 切点抽象:具体到某个class,是否具备方法、字段、注解访问的能力。
- 通知类型抽象:是否直接支持在方法前、后、中直接织入代码。
当然不具备能力不代表不能做AOP编程,可以通过其他方法解决,只是易用性的问题。
APT
APT 是 Annotation Processing Tool 的缩写,即注解处理工具。APT 是一种工具,用于在编译时处理 Java 源代码中的注解,生成额外的 Java 代码或者其他文件,以帮助开发者完成一些重复性的工作或者自动生成一些代码。
通过 APT,开发者可以定义自己的注解,并编写相应的处理器来处理这些注解。在编译时,APT 会扫描源代码中的注解,并调用相应的处理器来生成额外的代码或者其他文件。这些生成的代码可以用于实现一些功能,如自动实现 Parcelable 接口、生成单例模式的代码、自动生成代理类等。
APT 在 Android 开发中有着广泛的应用,例如 ButterKnife 库就是利用 APT 实现的,它通过在 View 属性上添加注解,然后在编译时生成相应的 View 绑定代码,从而简化了代码编写。另外,ButterKnife、Dagger等一些常用的 Android 库也使用了 APT 来生成代码。
总的来说,APT 是一种强大的工具,可以在编译时自动化处理一些重复性的工作,提高了开发效率,减少了开发者的工作量。
AspectJ
AspectJ 是一个基于 Java 语言的面向切面编程(AOP)的扩展框架,它提供了一种简洁而强大的方式来实现横切关注点的模块化和重用。
AspectJ 扩展了 Java 语言,引入了一组新的关键字和语法规则,使得开发者可以更方便地定义切面和通知,并将它们应用到代码中的特定连接点上。AspectJ 通过编译时的静态代码织入技术,将切面的行为嵌入到应用程序的字节码中,从而实现了对横切关注点的有效管理和控制。
使用 AspectJ,开发者可以通过注解或者 XML 配置文件来定义切面,并使用 Pointcut 表达式来指定切入点(即横切关注点所在的位置)。然后,开发者可以编写 Advice(通知)来定义切面在连接点处执行的代码逻辑,例如在方法调用之前或之后执行的操作。
举个简单的例子,假设我们想统计所有view的点击事件,使用AspectJ只需要写一个类即可。
1 |
|
javassit
Javassist 是一个 Java 字节码操作库,它提供了一种方便的方式来动态修改类的字节码,生成新的类或者修改已有类的行为。Javassist 的全称是 Java Programming Assistant,它允许开发者在运行时动态地创建、修改和操作 Java 类,而无需直接操作字节码。
Javassist 提供了一组简洁而强大的 API,使得开发者可以在运行时对类进行各种操作,例如创建新的类、修改已有类的字段、方法或者构造函数、添加新的方法、修改方法的实现、以及执行类的加载和热替换等。通过 Javassist,开发者可以实现诸如 AOP、动态代理、类加载器、字节码增强等各种功能。
以下是 Javassist 的简单用例,演示了如何使用 Javassist 创建一个新的类,并添加一个新的方法:
1 | import javassist.*; |
ASM
ASM 是一个 Java 字节码操作和分析框架,它允许开发者在编译时或运行时动态生成或修改 Java 类的字节码。ASM 的全称是 Java Bytecode Engineering Library,它提供了一组简单而强大的 API,使得开发者可以直接操作字节码,从而实现对 Java 类的各种高级操作。
ASM 具有轻量级、高性能和灵活性的特点,它的设计目标是尽可能地接近 Java 字节码的底层结构,以最小的开销实现对字节码的操作。ASM 提供了一种基于访问者模式的 API,开发者可以通过实现不同的访问者接口来遍历、解析和修改类的字节码,从而实现对类的各种操作,如新增、删除、修改字段、方法、注解等。
ASM 可以广泛应用于 Java 字节码的动态生成、增强和优化等领域,例如动态代理、AOP(面向切面编程)、代码生成器、类加载器、代码混淆、性能优化等。由于其高性能和灵活性,ASM 被广泛应用于各种 Java 框架和工具中,如 Spring、Hibernate、Mockito 等。
完整的学习ASM必须了解字节码和JVM相关知识。比如要织入一句简单的日志输出:
1 | Log.d("tag", " onCreate"); |
使用ASM编写是下面这个样子:
1 | mv.visitLdcInsn("tag"); |
可以看出ASM与AspectJ有很大的不同,AspectJ织入的代码就是实际编写的代码,但ASM必须使用其提供的API编写指令。一行java代码可能对应多行ASM API代码,因为一行java代码背后可能隐藏这多个JVM指令。
动态代理
动态代理是代理模式的一种实现,用于在运行时动态增强原始类的行为,实现方式是运行时直接生成class字节码并将其加载进虚拟机。
JDK本身就提供一个Proxy类用于实现动态代理。 我们通常使用下面的API创建代理类。
1 | //java.lang.reflect.Proxy |
其中在InvocationHandler实现类中定义核心切点代码。
1 | public class InvocationHandlerImpl implements InvocationHandler { |
java动态代理最大的问题是只能代理接口,而不能代理普通类或者抽象类,这是因为默认创建的代理类继承Porxy,而java又不支持多继承,这一点极大的限制了动态代理的使用场景,cglib可代理普通类。
AOP框架的特点及优劣势


