运行-time AOP vs Compile-time AOP
Run-time AOP vs Compile-time AOP
这两种AOP框架的优缺点是什么?我正在使用 Unity 作为我的 aop 框架,但我猜像 postsharp 这样的编译时 aop 框架可能比 运行-time aop 框架有更好的性能?貌似运行-time aop framework是用反射来实现注入的
我不是 .NET 专家,但我了解 Java 生态系统中的 AOP 世界,尤其是 AspectJ 和 Spring AOP。基本上有4种方面编织:
- 源代码编织:方面代码作为源代码语句注入到您的应用程序源代码中。这是某种预处理器方法。现在 Java 世界中没有 AOP 框架使用这种方法,但在 AOP 的早期曾经有过一些。
- 优点是在运行时完全独立于任何运行时库或特殊的 AOP 编译器,如果做得好的话。
- 缺点是源代码臃肿和编译前的预处理/代码生成步骤。您总是需要生成的源代码进行调试。
- 编译时编织: 方面代码由特殊的编译器编织到您的应用程序中。
- 优点是方面编织没有运行时开销。您唯一需要的是 class 路径上的小型运行时库。
- 缺点是,如果您想将方面编织到您的应用程序中,则您不能将决定完全推迟到运行时。但这只是在处理并不总是需要的调试或跟踪方面时的问题。另一个缺点是这种方法仅适用于您控制下的代码,即您需要拥有源代码。它不适用于第 3 方库。
- 二进制编织: 方面代码在编译后而不是在编译期间编织到现有 class 文件中。
- 优点是它也适用于您没有源代码的第 3 方代码。这种方法也可以与编译时编织混合使用。您还可以避免加载时编织的开销(见下文)。
- 缺点类似于编译时编织:一旦某个方面被编织到代码中,您就不能取消应用它,只能通过
if()
等切入点停用它的执行。但这可能非常有效。
- 加载时编织 (LTW): 编织 agent/library 在您的 VM/container 启动时提前加载。它获取一个配置文件,其中包含描述哪些方面应该被编织到哪些 classes 中的规则。
- 优点是可以动态决定if/what进行编织。如果通过字节码转换而不是通过动态代理或反射(见下文)完成,生成的字节码与通过编译时或二进制编织创建的字节码同样有效。另一个优点是,与二进制编织一样,它适用于您自己的代码以及第三方代码,只要编织代理可以 "see" 它,即它发生在子 classloader.
- 缺点是应用程序启动期间的一次性织入开销,因为织入是在 class加载时完成的。
- 基于代理的 LTW: 这种特殊的 LTW 形式由 Spring AOP 使用,而 AspectJ 执行上面列出的前 3 种形式。它通过为方面目标创建动态代理(即 subclasses 或接口实现)来工作。
- 除了您选择的框架(例如Spring)恰好支持它之外,我想不出任何特殊优势。
- 缺点是 public、非静态方法和基于代理的方法导致的运行时开销的限制。它也不捕获内部方法调用,即当代理 class 调用它自己的方法之一时,因为这些调用未被代理捕获。不支持特殊类型的切入点,例如构造函数拦截、成员变量 read/write 访问等等,这更像是一种 "AOP lite" 方法。但它足以满足您的目的。
通常,好的方面编译器(例如 AspectJ)会创建非常高效的字节码,并且在运行时不会严重依赖反射。如果您选择的方面框架确实依赖于反射,那么它可能不是很快。但也许它足够快,具体取决于您使用方面的程度。
可能我已经写得太多了,但我可以写得更多。这就是我现在停下来的原因。此外,这种问题不太适合 Whosebug,因为它可能导致哲学讨论和基于观点的辩论。即便如此,我希望我能做到公平 objective/impartial。
这两种AOP框架的优缺点是什么?我正在使用 Unity 作为我的 aop 框架,但我猜像 postsharp 这样的编译时 aop 框架可能比 运行-time aop 框架有更好的性能?貌似运行-time aop framework是用反射来实现注入的
我不是 .NET 专家,但我了解 Java 生态系统中的 AOP 世界,尤其是 AspectJ 和 Spring AOP。基本上有4种方面编织:
- 源代码编织:方面代码作为源代码语句注入到您的应用程序源代码中。这是某种预处理器方法。现在 Java 世界中没有 AOP 框架使用这种方法,但在 AOP 的早期曾经有过一些。
- 优点是在运行时完全独立于任何运行时库或特殊的 AOP 编译器,如果做得好的话。
- 缺点是源代码臃肿和编译前的预处理/代码生成步骤。您总是需要生成的源代码进行调试。
- 编译时编织: 方面代码由特殊的编译器编织到您的应用程序中。
- 优点是方面编织没有运行时开销。您唯一需要的是 class 路径上的小型运行时库。
- 缺点是,如果您想将方面编织到您的应用程序中,则您不能将决定完全推迟到运行时。但这只是在处理并不总是需要的调试或跟踪方面时的问题。另一个缺点是这种方法仅适用于您控制下的代码,即您需要拥有源代码。它不适用于第 3 方库。
- 二进制编织: 方面代码在编译后而不是在编译期间编织到现有 class 文件中。
- 优点是它也适用于您没有源代码的第 3 方代码。这种方法也可以与编译时编织混合使用。您还可以避免加载时编织的开销(见下文)。
- 缺点类似于编译时编织:一旦某个方面被编织到代码中,您就不能取消应用它,只能通过
if()
等切入点停用它的执行。但这可能非常有效。
- 加载时编织 (LTW): 编织 agent/library 在您的 VM/container 启动时提前加载。它获取一个配置文件,其中包含描述哪些方面应该被编织到哪些 classes 中的规则。
- 优点是可以动态决定if/what进行编织。如果通过字节码转换而不是通过动态代理或反射(见下文)完成,生成的字节码与通过编译时或二进制编织创建的字节码同样有效。另一个优点是,与二进制编织一样,它适用于您自己的代码以及第三方代码,只要编织代理可以 "see" 它,即它发生在子 classloader.
- 缺点是应用程序启动期间的一次性织入开销,因为织入是在 class加载时完成的。
- 基于代理的 LTW: 这种特殊的 LTW 形式由 Spring AOP 使用,而 AspectJ 执行上面列出的前 3 种形式。它通过为方面目标创建动态代理(即 subclasses 或接口实现)来工作。
- 除了您选择的框架(例如Spring)恰好支持它之外,我想不出任何特殊优势。
- 缺点是 public、非静态方法和基于代理的方法导致的运行时开销的限制。它也不捕获内部方法调用,即当代理 class 调用它自己的方法之一时,因为这些调用未被代理捕获。不支持特殊类型的切入点,例如构造函数拦截、成员变量 read/write 访问等等,这更像是一种 "AOP lite" 方法。但它足以满足您的目的。
通常,好的方面编译器(例如 AspectJ)会创建非常高效的字节码,并且在运行时不会严重依赖反射。如果您选择的方面框架确实依赖于反射,那么它可能不是很快。但也许它足够快,具体取决于您使用方面的程度。
可能我已经写得太多了,但我可以写得更多。这就是我现在停下来的原因。此外,这种问题不太适合 Whosebug,因为它可能导致哲学讨论和基于观点的辩论。即便如此,我希望我能做到公平 objective/impartial。