有没有替代cglib?

只是出于好奇,是否有任何(稳定)开源项目运行java代码生成而不是cglib? 我为什么要使用它们?

ASM java-asm

CGLIB和其他几乎所有的图书馆都build立在ASM的基础之上,ASM本身的行为水平非常低。 这对大多数人来说是一个阻碍,因为你必须理解字节码和JVMS的一点点才能正确使用它。 但是掌握ASM是非常有趣的。 但是请注意,虽然有很好的 ASM 4指南 ,但在API的某些部分,javadoc文档可能非常简洁,但是正在改进。 它紧密地遵循JVM版本来支持新function。

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

这个项目定期更新; 在这个编辑版本5.0.4的时候是2015年5月15日发布的。

字节好友 字节好友

Byte Buddy是一个相当新的库,但提供了CGLIB或Javassist提供的任何function。 Byte Buddy可以完全定制到字节代码级别,并带有一个expression性的特定领域的语言,允许非常可读的代码。

  • 它支持所有的JVM字节码版本,包括一些关于默认方法的操作码的Java 8语义变化。
  • ByteBuddy似乎没有受到其他图书馆的弊端
  • 高度可configuration
  • 相当快( 基准 代码 )
  • types安全stream畅的API
  • 键入安全callback

    Javassistbuild议或自定义检测代码基于普通String代码,因此在此代码中进行types检查和debugging是不可能的,而ByteBuddy允许使用纯Java编写这些代码,从而强制执行types检查并允许debugging。

  • 注释驱动(灵活)

    用户callback可以configuration注释,允许在callback中接收想要的参数。

  • 作为代理提供

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

  • 非常有据可查
  • 很多例子
  • 清洁的代码,〜94%的testing覆盖率
  • Android DEX支持

主要的缺点也许是API对于初学者来说有点冗长,但是它被devise成一个可选代理API,作为代理生成DSL; 没有魔法或可疑的默认值。 在操纵字节码时,它可能是最安全和最合理的select。 另外有多个例子和一个大教程,这不是一个真正的问题。

2015年10月,这个项目获得了Oracle公爵的select奖 。 在这个时候,它刚刚达到1.0.0里程碑 ,这是一个相当成就的。

请注意, mockito已经在2.1.0版中由Byte Buddyreplace了CGLIB 。

Javassist javassist

Javassist的javadoc比CGLIB好。 类工程API是可以的,但Javassist也不完美。 尤其是,与CGLIB的Enhancer相当的ProxyFactory也有一些缺点,仅列举几个:

  • Bridge方法不完全支持(即为协变返回types生成的方法)
  • ClassloaderProvider是一个静态字段,它应用于同一个类加载器中的所有实例
  • 自定义命名可能是受欢迎的(检查签名的jar子)
  • 没有任何扩展点,几乎所有感兴趣的方法都是私有的,如果我们想要改变一些行为,这是很麻烦的
  • 虽然Javassist在类中提供对注释属性的支持,但它们在ProxyFactory不受支持。

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

  • 方面代码是用操作码编译的纯Javastring编写
  • 没有types检查
  • 没有generics
  • 没有拉姆达
  • 没有自动(联合)拳击

另外Javassist被认为比Cglib慢。 这主要是由于它读取类文件的方法而不是读取像CGLIB那样的加载类。 而实施本身很难被理解为公平的; 如果需要对Javassist代码进行更改,则有很多机会可能会破坏某些东西。

Javassist也不受干扰,他们转向github大约在2013年似乎已经certificate是有用的,因为它显示了社区的定期提交和请求。

这些限制仍然在版本3.17.1。 版本已经碰到3.20.0版本,但似乎Javassist可能仍然有Java 8支持的问题。

JiteScript

JiteScript看起来像是一个很好地塑造ASM DSL的新作,它基于最新的ASM版本(4.0)。 代码看起来很干净。

但是这个项目还处于初期阶段,因此API /行为可能会发生变化,而且文档是可怕的。 如果不放弃,更新稀less。

Proxetta jodd

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

虽然这个比较less见,但如果运作良好的话,还是不存在的。 在处理字节码的时候有很多的情况需要处理。

AspectJ aspectj

AspectJ是一个非常强大的面向方面编程的工具(仅)。 AspectJ操纵字节码来达到目标​​,这样你就可以用它实现你的目标。 但是,这需要在编译时进行操作。 spring提供加载时通过代理从2.5,4.1.x版本编织。

CGLIB cglib

自从这个问题以来,已经更新了关于CGLIB的一句话。

CGLIB速度很快,这是它现在仍然存在的主要原因之一,而CGLIB迄今为止的工作几乎比其他scheme(2014-2015)更好。

一般来说,允许在运行时重写类的库必须避免在重写对应的类之前加载任何types的类。 因此,他们不能使用JavareflectionAPI,它需要加载reflection中使用的任何types。 相反,他们必须通过IO(这是一个性能破坏者)读取类文件。 这使得例如Javassist或Proxetta明显比Cglib慢,它只是通过reflectionAPI读取方法并覆盖它们。

但是,CGLIB不再处于积极的发展之中。 有近期发布的版本,但这些变化被许多人认为是微不足道的,大多数人从来没有更新到第3版,因为CGLIB在最近发布的版本中引入了一些严重的错误 ,但是没有真正build立起信心。 版本3.1修复了版本3.0的许多困境(自版本4.0.3 Spring框架重新包装版本3.1 )。

另外,CGLIB源代码的质量相当差 ,所以我们看不到新开发者joinCGLIB项目。 对于CGLIB的活跃印象,请参阅他们的邮件列表 。

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

目前正在使用3.2.0版本,他们将重点放在Java 8上,但到目前为止,想要支持Java 8的用户必须在构build时使用技巧。 但进展非常缓慢。

而CGLIB仍然被认为是PermGen内存泄露的困扰。 但其他项目可能没有经过多年的战斗testing。

编译时间标注处理 标注处理

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

这是从单独的命令行工具附带的Java 5开始的,用于处理注释: apt ,并且从Java 6开始,注释处理被集成到Java编译器中。

在某个时候,你需要明确地通过处理器,现在使用ServiceLoader方法(只需将这个文件META-INF/services/javax.annotation.processing.Processor到jar中),编译器就可以自动检测注释处理器。

这种代码生成的方法也有缺点,它需要大量的工作和对Java语言而不是字节码的理解。 这个API有点麻烦,因为一个是编译器的插件,所以必须非常小心,使这个代码成为最有弹性和用户友好的错误信息。

这里最大的优点就是在运行时避免了另一个依赖,可以避免内存泄露。 而且完全可以控制生成的代码。

结论

在2002年, CGLIB定义了一个新的标准来轻松操纵字节码。 我们现在所使用的许多工具和方法(CI,覆盖范围,TDD等)在当时还没有成熟或不成熟。 CGLIB十多年来一直是相关的; 这是一个相当不错的成就。 它比使用操作码直接快,并且使用简单的API。

它定义了关于代码生成的新标准,但是现在不再是了,因为环境和要求已经改变,标准和目标也是如此。

JVM在最近和未来的Java(7/8/9/10)版本(invokedynamic,缺省方法,值types等)中发生了变化并将发生变化。 ASM定期升级他的API和内部以遵循这些更改,但CGLIB和其他人尚未使用它们。

虽然注释处理越来越引人注目,但并不像运行时生成那样灵活。

截至2015年, Byte Buddy虽然在现场颇为新鲜 – 为运行时代提供了最引人注目的卖点 。 一个体面的更新率,作者有一个Java字节码内部知识。

Javassist 。

如果您需要制作代理,请查看commons-proxy – 它同时使用CGLIB和Javassit。

我更喜欢原始的ASM ,我相信它是由cglib使用的。 这是低级的,但文件是辉煌的 ,一旦你习惯了它,你会飞。

要回答第二个问题,当您的reflection和dynamic代理开始感觉有点拼凑在一起,并且您需要坚如磐石的解决scheme时,您应该使用代码生成。 在过去,我甚至在Eclipse的构build过程中添加了代码生成步骤,有效地为我编译时间报告任何事情。

我认为使用Javassist而不是cglib更有意义。 例如javasist完全可以与cglib不同的签名jar子工作。 另外,像Hibernate这样盛大的项目决定停止使用cglib来支持Javassist 。

Proxetta是另一个不太知道的java代理框架。

CGLIB是十多年前在AOP和ORM时代devise和实施的。 目前我没有理由使用它,我不再维护这个库(除了我的遗留应用程序的错误修复)。 实际上我见过的所有CGLIB用例都是现代编程中的反模式。 通过任何JVM脚本语言(例如groovy)来实现相同的function应该是微不足道的。