AspectJ 是面向切面编程(AOP)在 Java 领域的标准实现。在全埋点场景中,AspectJ 可以在编译期将埋点代码织入指定的方法(如 View.OnClickListener.onClick()),实现零业务代码侵入的全自动埋点。
一、AspectJ 核心概念
- Join Point:代码中的可插入点,如方法调用、字段赋值等。
- Pointcut:匹配 Join Point 的表达式,定义”在哪里”插入代码。
- Advice:在匹配的 Pointcut 前后执行的代码,即”插入什么逻辑”。
@Before:方法执行前@After:方法执行后@Around:包裹方法执行,可控制是否执行原方法
二、Gradle 集成
// 项目根 build.gradle |
三、点击埋点的 Aspect 实现
|
四、更丰富的 Pointcut 场景
// 匹配所有 Activity 的 onCreate(页面进入埋点) |
五、AspectJ vs 其他方案
| 方案 | 织入时机 | 覆盖度 | 性能 | 集成复杂度 |
|---|---|---|---|---|
| AspectJ | 编译期 | 高(所有 onClick) | 无运行时开销 | 中(Gradle 插件) |
| ASM | 编译期(Transform) | 极高 | 无运行时开销 | 高(需字节码知识) |
| Javassist | 编译期/运行时 | 高 | 有反射开销 | 低(源码级 API) |
| OnClickListener 代理 | 运行时 | 中 | 极低 | 低 |
面试常考问题
Q1:AspectJ 的织入时机是在 javac 编译之前还是之后?
AspectJ 在 Java 编译为 .class 文件之后、.class 打包为 .dex 之前执行。它修改的是字节码。在 Android Gradle 构建流程中,AspectJX 插件在 Transform 阶段插入,扫描所有 class 文件,对匹配 Pointcut 的方法进行字节码增强。
Q2:@Around 与 @Before/@After 的选择?
全埋点场景推荐 @Around,因为可以在 joinPoint.proceed() 前后分别采集时间,计算点击处理的耗时,同时能通过 try-catch 捕获原始 onClick 中的异常并上报。@Before 无法获取方法返回值或捕获异常。
Q3:AspectJ 如何处理 Kotlin 的 lambda onClick?
Kotlin 中 view.setOnClickListener { } 编译后生成实现 View.OnClickListener 的匿名内部类,其 onClick 方法符合 Pointcut execution(* View.OnClickListener.onClick(View)),可以正常织入。反编译后可见 class 文件中实际生成了一个额外类,AspectJ 对其字节码进行修改。


