问题背景
我们在实现一个自动化录制回放的过程中发现,需要去修改 Flutter 框架( Dart 层面)的代码才能够满足要求,这就会有了对框架的侵入性。要解决这种侵入性的问题,更好地减少迭代过程中的维护成本,我们考虑的首要方案即面向切面编程。
那么如何解决 AOP for Flutter 这个问题呢?本文将重点介绍一个闲鱼技术团队开发的针对 Dart 的 AOP 编程框架 AspectD。
AspectD:面向 Dart 的 AOP 框架
AOP 能力究竟是运行时还是编译时支持依赖于语言本身的特点。举例来说在 iOS 中,Objective C 本身提供了强大的运行时和动态性使得运行期 AOP 简单易用。在 Android下,Java 语言的特点不仅可以实现类似 AspectJ 这样的基于字节码修改的编译期静态代理,也可以实现 Spring AOP 这样的基于运行时增强的运行期动态代理。那么 Dart 呢?一来 Dart 的反射支持很弱,只支持了检查( Introspection ),不支持修改( Modification );其次 Flutter 为了包大小,健壮性等的原因禁止了反射。
因此,我们设计实现了基于编译期修改的 AOP 方案 AspectD。
1、设计详图
2、典型的 AOP 场景
下列 AspectD 代码说明了一个典型的 AOP 使用场景:aop.dartimport 'package:example/main.dart' as app;import 'aop_impl.dart';void main()=> app.main();
aop_impl.dartimport 'package:aspectd/aspectd.dart';@Aspect()@pragma("vm:entry-point")class ExecuteDemo {@pragma("vm:entry-point")
ExecuteDemo();@Execute("package:example/main.dart", "_MyHomePageState", "-_incrementCounter")@pragma("vm:entry-point")void _incrementCounter(PointCut pointcut) {
pointcut.proceed();
print('KWLM called!');
}
}
3、面向开发者的API设计
PointCut 的设计@Call("package:app/calculator.dart","Calculator","-getCurTime")
PointCut 需要完备表征以什么样的方式( Call/Execute 等),向哪个 Library,哪个类(Library Method 的时候此项为空),哪个方法来添加 AOP 逻辑。PointCut 的数据结构:@pragma('vm:entry-point')class PointCut {final Map sourceInfos;final Object target;final String function;final String stubId;final List positionalParams;final Map namedParams;@pragma('vm:entry-point')
PointCut(this.sourceInfos, this.target, this.function, this.stubId,this.positionalParams, this.namedParams);@pragma('vm:entry-point')Object proceed(){return null;
}