有没有 cglib 的替代品? [关闭]
Posted
技术标签:
【中文标题】有没有 cglib 的替代品? [关闭]【英文标题】:Are there alternatives to cglib? [closed] 【发布时间】:2011-01-16 17:52:18 【问题描述】:出于好奇,除了 cglib 之外,还有其他(稳定的)用于运行时 java 代码生成的开源项目吗?我为什么要使用它们?
【问题讨论】:
你需要替代方法来做 cglib 不能做的事情吗? 我对一种可扩展的 ORM 有一些想法,但是运行时代码生成是必不可少的,因为每个类都会完全生成。我还没用过cglib,但是知道hibernate和spring都用过,所以这是我的第一个想法。但如果还有其他更适合或更容易使用的东西,我愿意接受。 我很高兴离题问题仍然被编入索引,因为这是一个很棒的参考。 【参考方案1】:ASMjava-asm
CGLIB 和几乎所有其他库都建立在 ASM 之上,而 ASM 本身的作用非常低。这对大多数人来说是一个阻碍,因为您必须了解字节码和一点JVMS 才能正确使用它。但是掌握 ASM 无疑是非常有趣的。但是请注意,虽然有一个 great ASM 4 guide,但在 API 的某些部分,如果它存在的话,javadoc 文档可以非常简洁,但它正在被改进。它紧跟 JVM 版本以支持新功能。
但是,如果您需要完全控制,ASM 是您的首选武器。
这个项目会定期更新;在本次编辑时,版本 5.0.4 于 2015 年 5 月 15 日发布。
Byte Buddybyte-buddy
Byte Buddy 是一个相当新的库,但提供了 CGLIB 或 Javassist 提供的任何功能等等。 Byte Buddy 可以完全自定义到字节码级别,并带有一种富有表现力的领域特定语言,允许非常可读的代码。
它支持所有 JVM 字节码版本,包括 Java 8 关于默认方法的一些操作码的语义更改。 ByteBuddy 似乎没有其他库的缺点 高度可配置 相当快(benchmarkcode) 类型安全流畅的 API类型安全的回调
Javassist 建议或自定义检测代码基于纯
String
中的代码,因此无法在此代码中进行类型检查和调试,而 ByteBuddy 允许使用纯 Java 编写这些代码,因此强制执行类型检查并允许调试。
注释驱动(灵活)
用户回调可以配置注释,允许在回调中接收想要的参数。
可代理
非常有据可查 很多例子 简洁的代码,~94% 的测试覆盖率 android DEX 支持漂亮的代理构建器允许将 ByteBuddy 用作纯代理或附加代理。它允许不同的种类
也许主要的缺点是,API 对初学者来说有点冗长,但它被设计为一个可选的 API,形状类似于代理生成 DSL;没有神奇或可疑的默认值。在操作字节码时,它可能是最安全、最合理的选择。还有多个示例和大型教程,这不是一个真正的问题。
2015 年 10 月,该项目收到了Oracle Duke's choice award。这时候才刚到1.0.0 milestone,已经是相当的成就了。
请注意,mockito 在版本 2.1.0 中已替换 CGLIB by Byte Buddy。
Javassistjavassist
Javassist 的 javadoc 比 CGLIB 好得多。类工程 API 还可以,但 Javassist 也不完美。特别是,与CGLIB 的Enhancer
等效的ProxyFactory
也存在一些缺点,仅举几例:
ClassloaderProvider
是一个静态字段,然后它适用于同一类加载器中的所有实例
可能会欢迎自定义命名(检查已签名的 jar)
没有扩展点,几乎所有感兴趣的方法都是私有的,如果我们想改变一些行为,这很麻烦
虽然 Javassist 支持类中的注释属性,但 ProxyFactory
不支持它们。
在面向方面方面,可以在代理中注入代码,但 Javassist 中的这种方法是有限的并且容易出错:
方面代码以纯 Java 字符串编写,该字符串在操作码中编译 没有类型检查 没有泛型 没有 lambda 没有自动(取消)装箱Javassist 也被认为比 Cglib 慢。这主要是由于它读取类文件而不是读取加载的类(如 CGLIB)的方法。而且implementation 本身很难公平地阅读;如果需要对 Javassist 代码进行更改,则有很多机会破坏某些东西。
Javassist 也受到不活动的影响,他们转移到 github circa 2013 似乎被证明是有用的,因为它显示了来自社区的定期提交和拉取请求。
这些限制在 3.17.1 版本中仍然存在。版本已升级到 3.20.0 版,但似乎 Javassist 可能仍然存在 Java 8 支持问题。
JiteScript
JiteScript 看起来确实是为 ASM 精心打造的新 DSL,它基于最新的 ASM 版本 (4.0)。代码看起来很干净。
但是该项目仍处于早期阶段,因此 API / 行为可能会发生变化,而且文档很糟糕。如果不放弃,更新很少。
Proxettajodd
这是一个相当新的工具,但它提供了迄今为止最好的 human API。它允许不同类型的代理,例如子类代理(cglib 方法)或编织或委托。
虽然这个比较少见,但如果它运作良好,则不存在任何信息。处理字节码时有很多极端情况需要处理。
AspectJaspectj
AspectJ 是一个非常强大的面向方面编程的工具(仅限)。 AspectJ 操纵字节码来实现它的目标,这样你就可以用它来实现你的目标。但是,这需要在编译时进行操作;自版本 2.5、4.1.x 起,spring 在加载时通过代理提供编织。
CGLIBcglib
关于 CGLIB 的一句话,自该问题提出以来已更新。
CGLIB 速度非常快,这是它仍然存在的主要原因之一,此外,CGLIB 的运行效果几乎比迄今为止(2014-2015 年)的任何替代品都要好。
一般来说,允许在运行时重写类的库必须避免在相应的类被重写之前加载任何类型。因此,它们不能使用要求加载反射中使用的任何类型的 Java 反射 API。相反,他们必须通过 IO 读取类文件(这是一个性能破坏者)。这使得 Javassist 或 Proxetta 比 Cglib 慢得多,后者只是通过反射 API 读取方法并覆盖它们。
但是,CGLIB 不再处于积极开发中。有最近的版本,但许多人认为这些变化微不足道,大多数人从未更新到版本 3,因为 CGLIB 在上一个版本中引入了一些 severe bugs 并没有真正建立信心。 版本 3.1 修复了很多3.0 版的困境(因为 4.0.3 版 Spring 框架重新打包了 version 3.1)。
此外,CGLIB 源代码相当 poor quality,因此我们看不到新的开发人员加入 CGLIB 项目。有关 CGLIB 活跃度的印象,请参阅他们的mailing list。
请注意,在proposition on the guice mailing list 之后,CGLIB 现在可以在github 上使用,以使社区能够更好地帮助该项目,它似乎正在工作(多次提交和拉取请求,ci,更新的 maven),但大多数问题仍然存在。
目前正在开发 3.2.0 版本,并且他们将精力集中在 Java 8 上,但到目前为止,想要支持 Java 8 的用户必须在构建时使用技巧。但进展非常缓慢。
而且众所周知,CGLIB 仍然受到 PermGen 内存泄漏的困扰。但其他项目可能这么多年都没有经过实战考验。
Compile time annotation Processingannotation-processing
这个当然不是runtime,而是生态系统的重要组成部分,大部分代码生成使用不需要runtime创建。
这从 Java 5 开始,它带有单独的命令行工具来处理注释:apt
,从 Java 6 开始,注释处理被集成到 Java 编译器中。
有时您需要显式传递处理器,现在使用 ServiceLoader
方法(只需将此文件 META-INF/services/javax.annotation.processing.Processor
添加到 jar 中)编译器可以自动检测注释处理器。
这种代码生成方法也有缺点,它需要大量工作和对 Java 语言而不是字节码的理解。这个 API 有点麻烦,而且作为编译器中的插件,必须非常小心地使这段代码成为最具弹性和用户友好的错误消息。
这里最大的好处是它在运行时避免了另一个依赖,你可以避免 permgen 内存泄漏。并且可以完全控制生成的代码。
结论
在2002CGLIB 中定义了一个新标准来轻松操作字节码。我们现在拥有的许多工具和方法(CI、覆盖率、TDD 等)当时不可用或不成熟。十多年来,CGLIB 一直保持相关性;这是一个相当不错的成就。与直接操作操作码相比,它速度快且 API 易于使用。
它定义了有关代码生成的新标准,但现在它不再是,因为环境和要求发生了变化,标准和目标也发生了变化。
JVM 发生了变化,并将在最近和未来的 Java (7/8/9/10) 版本(invokedynamic、默认方法、值类型等)中发生变化。 ASM 定期升级他的 API 和内部结构以跟踪这些变化,但 CGLIB 和其他人尚未使用它们。
虽然注释处理越来越受欢迎,但它不如运行时生成灵活。
截至 2015 年,Byte Buddy - 虽然在现场相当新 - 为运行时生成提供了最引人注目的卖点。不错的更新率,而且作者对 Java 字节码的内部结构了如指掌。
【讨论】:
不要被 ASM 的低级特性所推迟。一个不需要太多字节码知识的好方法是使用包含的 ASM-ifier,它检查一个类并输出 ASM 代码,运行时会生成该类!【参考方案2】:Javassist.
如果您需要制作代理,请查看commons-proxy - 它同时使用 CGLIB 和 Javassit。
【讨论】:
【参考方案3】:我更喜欢 raw ASM,我相信无论如何 cglib 都会使用它。它的级别很低,但文档精彩,一旦你习惯了它,你就会飞起来。
要回答您的第二个问题,当您的反射和动态代理开始感觉有点拼凑在一起并且您需要一个坚如磐石的解决方案时,您应该使用代码生成。在过去,我什至在 Eclipse 的构建过程中添加了代码生成步骤,有效地为我提供了任何东西的编译时间报告。
【讨论】:
【参考方案4】:我认为使用Javassist 代替cglib 更有意义。例如。与 cglib 不同,javasist 与签名 jar 完美配合。另外还有Hibernate项目decided to stop using cglib in favor of Javassist等宏大的项目。
【讨论】:
【参考方案5】:CGLIB 是十多年前在 AOP 和 ORM 时代设计和实现的。 目前我认为没有理由使用它,并且我不再维护这个库(除了我的遗留应用程序的错误修复)。 实际上,我见过的所有 CGLIB 用例都是现代编程中的反模式。 通过任何 JVM 脚本语言实现相同的功能应该是微不足道的,例如时髦的。
【讨论】:
这是完全错误的。如果您在运行前不知道这些类,则无法使用 Groovy 或任何语言创建 Java 类的代理。这是不可能的,因为 Java 字节码是类型安全的。运行时代码生成对于 JVM 来说是一件大事,甚至还有 JCL 支持。以上是关于有没有 cglib 的替代品? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
有没有支持 JDBC 的 Google App Engine 的替代品? [关闭]