小编典典

有cglib的替代品吗?

java

出于好奇,除了cglib之外,是否有任何(稳定)开源项目用于运行时Java代码生成?我为什么要使用它们?


阅读 532

收藏
2020-09-28

共1个答案

小编典典

ASM [java-asm](file:///C:/questions/tagged/java-asm

“显示标记为“ java-asm”的问题”)

CGLIB和几乎所有其他库都建立在ASM之上,而ASM本身的作用很低。对于大多数人来说,这是一个阻止因素,因为您必须了解字节码和一些JVMS才能正确使用它。但是掌握ASM无疑是非常有趣的。但是请注意,尽管有
出色的 ASM4指南,但在API的某些部分中,javadoc文档可能非常简洁(如果存在的话),但仍在改进中。它紧密遵循JVM版本以支持新功能。

但是,如果您需要完全控制,ASM是您的首选武器。

该项目定期更新; 在2015年5月15日发布此编辑版本5.0.4时。

字节伙伴 字节伙伴

Byte Buddy是一个相当新的库,但是提供了CGLIB或Javassist提供的任何功能以及更多功能。Byte
Buddy可以完全自定义到字节代码级别,并带有一种表达性强的领域特定语言,允许非常可读的代码。

  • 它支持所有JVM字节码版本,包括有关默认方法的某些操作码的Java 8语义更改。
  • ByteBuddy似乎没有遭受其他库所具有的缺点的困扰
  • 高度可配置
  • 相当快(基准 代码
  • 类型安全的流利API
  • 输入安全回调

Javassist的建议或自定义工具代码基于简单的代码,String因此无法在此代码中进行类型检查和调试,而ByteBuddy允许使用纯Java编写代码并因此进行类型检查并允许调试。

  • 注释驱动(灵活)

用户回调可以配置有注释,以允许在回调中接收所需的参数。

  • 可作为代理

漂亮的代理构建器允许ByteBuddy用作纯代理或附加代理。它允许不同的种类

  • 很好的记录
  • 很多例子
  • 干净的代码,约94%的测试覆盖率
  • Android DEX支持

该API的主要缺点也许是,对于初学者来说有点冗长,但它被设计为一种像代理代DSL一样的可选API;没有魔术或可疑的默认值。在处理字节码时,它可能是最安全,最合理的选择。同样,有多个示例和大量教程,这不是真正的问题。

在2015年10月,该项目获得了OracleDuke的选择奖。此时,它刚刚达到了1.0.0里程碑,这是一个很大的成就。

请注意,mockito已在2.1.0版中将ByteBuddy替换为CGLIB

Javassist进行

Javassist

Javassist的Javadoc比CGLIB更好。类工程API可以,但是Javassist也不完美。特别是,ProxyFactory它等同于CGLIB的Enhancer缺点,仅列举了一些缺点:

  • 不完全支持Bridge方法(即针对协变返回类型生成的方法)
  • ClassloaderProvider 是一个静态字段,那么它适用于同一类加载器中的所有实例
  • 自定义命名可能会受到欢迎(带签名罐子的支票)
  • 没有扩展点,几乎所有感兴趣的方法都是私有的,如果我们要更改某些行为,这将很麻烦
  • 尽管Javassist支持类中的注释属性,但中不支持它们ProxyFactory

在面向方面方面,可以将代码注入代理中,但是Javassist中的这种方法是有限的,并且容易出错:

  • 方面代码以纯Java字符串 编写 ,并以操作码 编译
  • 没有类型检查
  • 没有泛型
  • 没有lambda
  • 没有自动装箱

Javassist也被认为比Cglib慢。这主要是由于其读取类文件的方法,而不是像CGLIB这样读取已加载的类的方法。而且实现本身很难理解为公平;
如果需要更改Javassist代码,则有很多机会破坏某些东西。

Javassist也遭受了不活动的困扰,他们被证明在2013年左右迁移到github似乎很有用,因为它显示了定期的提交和来自社区的请求。

这些限制仍然存在于3.17.1版中。该版本已被升级到3.20.0版本,但是Javassist似乎仍然对Java 8支持存在问题。

JiteScript

JiteScript看起来确实是为ASM精心设计DSL的新作品,它基于最新的ASM版本(4.0)。该代码看起来很干净。

但是 该项目尚处于早期阶段,因此可以更改API /行为,而且文档非常糟糕。并更新稀缺,如果不放弃。

普罗塞塔·

乔德

这是一个相当新的工具,但是它提供了迄今为止最好的 人工 API。它允许使用不同类型的代理,例如子类代理(cglib方法)或编织或委派。

尽管这是相当少见的,但如果运作良好,则没有任何信息。在处理字节码时,有很多特殊情况需要处理。

AspectJ中

AspectJ

AspectJ是用于 面向方面的编程
的非常强大的工具(仅)。AspectJ通过操纵字节码来实现其目标,以便您可以由此实现目标。但是,这需要在编译时进行操作。从2.54.1.x开始,Spring通过代理在加载时进行编织。

CGLIB

cglib

自问这个问题以来,有关CGLIB的词汇已更新。

CGLIB的运行速度非常快,这是它仍然存在的主要原因之一,而且CGLIB的运行几乎比现在(2014-2015年)的任何替代产品都要好。

一般来说,允许在运行时重写类的库必须避免在重写相应类之前加载任何类型。因此,它们不能使用Java反射API,而Java反射API要求加载反射中使用的任何类型。相反,他们必须通过IO(这是性能破坏者)读取类文件。例如,这使得Javassist或Proxetta比Cglib慢得多,后者仅通过反射API读取方法并覆盖它们。

但是,CGLIB不再处于积极开发中。 有最新版本,但是许多人认为这些更改微不足道,并且大多数人从未更新到版本3,因为CGLIB
在最新版本中引入了一些严重的错误,但实际上并没有建立信心。
3.1版解决了3.0版的许多问题(因为4.0.3版Spring框架重新打包了3.1版)。

而且,CGLIB源代码的质量很差,因此我们看不到新的开发人员加入CGLIB项目。要了解CGLIB的活跃性,请参阅他们的邮件列表

请注意,遵循guice邮件列表上的命题之后,CGLIB现在可以在github上使用,以使社区能够更好地帮助该项目,它似乎正在工作(多次提交和请求,ci,更新的maven),但仍然存在大多数问题。

目前,正在开发3.2.0版,并且他们将精力集中在Java 8上,但是到目前为止,想要Java 8支持的用户必须在构建时使用技巧。但是进展非常缓慢。

CGLIB仍然因PermGen内存泄漏而困扰。但是其他项目可能没有经过这么多年的战斗测试。

编译时注释处理
注释处理

当然,这不是运行时,而是生态系统的重要组成部分,大多数代码生成用法不需要​​创建运行时。

从Java 5开始,Java 5带有单独的命令行工具来处理批注:apt,从Java 6开始,批注处理已集成到Java编译器中。

在某些时候,您需要显式地传递处理器,现在通过这种ServiceLoader方法(只需将此文件添加META- INF/services/javax.annotation.processing.Processor到jar中),编译器就可以自动检测注释处理器。

这种代码生成方法也有缺点,它也需要大量工作,并且需要理解Java语言而不是字节码。该API有点麻烦,并且因为它是编译器中的插件,因此必须格外小心,以使该代码成为最有弹性和用户友好的错误消息。

这里最大的好处是,它避免了运行时的另一个依赖关系,可以避免permgen内存泄漏。并且可以完全控制生成的代码。

结论

2002年
CGLIB定义了一个新的标准来轻松地操作字节码。当今我们拥有的许多工具和方法(CI,覆盖范围,TDD等)当时都不可用或尚未成熟。十多年来,CGLIB一直与时俱进;
这是一个相当不错的成就。与直接操作操作码相比,它的运行速度快且具有易于使用的API。

它定义了有关代码生成的新标准,但如今由于环境和要求已发生变化,因此不再存在,标准和目标也已发生变化。

JVM已更改,并将在最近和将来的Java(7/8/9/10)版本(调用动态,默认方法,值类型等)中更改。ASM定期升级他的API和内部组件以适应这些更改,但是CGLIB和其他人员尚未使用它们。

尽管注释处理越来越受青睐,但它不像运行时生成那样灵活。

截至2015年, 虽然 Byte Buddy刚刚出现但它 为运行时生成提供了最引人注目的 卖点
。相当不错的更新速度,并且作者对Java字节码内部知识非常了解。

2020-09-28