动态 Java 字节码操作框架比较
有一些用于动态字节码生成、操作和编织的框架(BCEL、CGLIB、javassist、ASM、MPS)。我想了解它们,但由于我没有太多时间了解它们的所有细节,我希望看到一种比较图表,说明其中一个与其他的优缺点以及解释为什么。
在 SO 中,我发现很多问题都提出类似的问题,答案通常是“你可以使用 cglib 或 ASM”,或者“javassist 比 cglib 更好”,或者“BCEL 已经过时了,正在消亡”或者“ASM 正在消失”。最好的,因为它给出了 X 和 Y”。这些答案很有用,但并没有在我想要的范围内完全回答问题,更深入地比较它们并给出每个答案的优点和缺点。
There are some frameworks out there for dynamic bytecode generation, manipulation and weaving (BCEL, CGLIB, javassist, ASM, MPS). I want to learn about them, but since I don't have much time to know all the details about all of them, I would like to see a sort of comparison chart saying the advantages and disadvantages of one versus the others and an explanation of why.
Here in SO, I found a lot of questions asking something similar, and the answers normally said "you can use cglib or ASM", or "javassist is better than cglib", or "BCEL is old and is dying" or "ASM is the best because it gives X and Y". These answers are useful, but does not fully answer the question in the scope that I want, comparing them more deeply and giving the advantages and disadvantages of each one.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
字节码库分析
正如我从您在此处获得的答案以及您所查看的问题中的答案可以看出的那样,这些答案并未以您所说的明确方式正式解决该问题。您要求进行比较,同时这些答案已经模糊地说明了根据您的目标可能想要什么(例如,您需要知道字节码吗?[y/n]),或者太狭窄。
这个答案是对每个字节码框架的简短分析,并在最后提供了快速比较。
Javassist
我个人更喜欢 Javassist,只是因为速度快您可以开始使用它并用它构建和操作类。 教程简单易懂。 jar 文件只有 707KB,非常方便携带;使其适合独立应用程序。
ASM
ObjectWeb 的 ASM 是一个非常全面的库,它不缺少与构建、生成和加载类相关的任何内容。事实上,它甚至具有带有预定义分析器的类分析工具。据说这是字节码操作的行业标准。这也是我回避它的原因。
当我看到 ASM 的示例时,它似乎是一项繁琐的任务,需要大量的行来修改或加载一个类。甚至某些方法的某些参数对于 Java 来说似乎有点神秘和不合适。有了像 ACC_PUBLIC 这样的东西,以及到处都有
null
的大量方法调用,老实说,它看起来确实更适合像 C 这样的低级语言。为什么不简单地直接使用传递一个像“public”这样的字符串文字,还是一个枚举Modifier.PUBLIC
?它更加友好且易于使用。然而,这是我的意见。作为参考,这里有一个 ASM (4.0) 教程: https://www.javacodegeeks.com/2012/02/manipulated-java-class-files-with-asm.html
BCEL
据我所知,这个库是您的基本类库,它可以让您完成您需要做的一切 — 如果您能抽出几个月或几个月的时间的话 年。
这里有一个 BCEL 教程,真正说明了这一点:http://www.geekyarticles.com/2011/08/manipulated-java-class-files-with-bcel.html?m=1
cglib
尽管您可以从类中读取信息,并且可以转换类,但该库似乎是为代理量身定制的。 教程都是关于代理的bean,它甚至提到它被使用通过“数据访问框架生成动态代理对象并拦截字段访问”。尽管如此,我认为您没有理由不能将它用于字节码操作而不是代理的更简单的目的。
ByteBuddy
长话短说,其中 BCEL缺乏,ByteBuddy 丰富。它使用一个名为 ByteBuddy 的主类,该类使用服务设计模式。您创建 ByteBuddy 的一个新实例,它代表您要修改的类。完成修改后,您可以使用
make()
创建一个DynamicType
。他们的网站上有完整的教程和 API 文档。目的似乎是为了进行相当高级的修改。当谈到方法时,除了委托方法之外,官方教程或任何第三方教程中似乎没有任何关于从头开始创建方法的内容(编辑,如果你知道这个方法在哪里)已解释)。
您可以在其网站上找到他们的教程。可以在此处找到一些示例。
Java 类助手 (jCLA)
我正在构建自己的字节码库,它将称为 Java 类Assistant,简称 jCLA,因为我正在开发的另一个项目以及 Javassist
的上述怪癖,但在完成之前我不会将其发布到 GitHub,但该项目目前可供浏览GitHub 并提供反馈,因为它目前处于 alpha 阶段,但仍然足以作为一个基本的类库(目前正在编译器上工作;如果可以的话请帮助我!它将很快发布!)。它将具有从 JAR 文件读取和写入类文件的能力,以及从源代码和类文件编译和反编译字节码的能力,这将是相当简单的。
整体使用模式使得使用 jCLA 相当容易,
尽管可能需要一些时间来适应,并且在用于类修改的方法和方法参数风格上显然与 ByteBuddy 非常相似:(VariableInitializer 生产规范方便起见。)
正如上面的代码片段所暗示的那样,每个
ClassDefinition
都是不可变的。这使得 jCLA 更加安全、线程安全、网络安全且易于使用。该系统主要围绕 ClassDefinitions 作为以高级方式查询类信息的选择对象,并且该系统的构建方式是 ClassDefinition 与目标类型(例如 ClassBuilder 和 ClassFile)之间进行转换。jCLA 对类数据使用分层系统。在底部,您有不可变的 ClassFile:类文件的结构或软件表示。然后,您就拥有了不可变的
ClassDefinition
,它们从 ClassFile 转换为对修改或从类中读取数据的程序员来说更不那么神秘、更易于管理和有用的东西,并且与通过访问的信息相当java.lang.Class
。最后,您有可变的 ClassBuilder。 ClassBuilder 是修改或创建类的方式。它允许您直接从构建器的当前状态创建一个ClassDefinition
。没有必要为每个类创建新的构建器,因为reset()
方法将清除变量。(该库的分析将在准备好发布后立即提供。)
但在那之前,截至今天:
我仍然不过建议学习一下java字节码。这将使调试更加容易。
比较
考虑到所有这些分析(目前不包括 jCLA),最广泛的框架是 ASM,最容易使用的是 Javassist,最基本的实现是 BCEL,最高级的字节码生成和代理是 cglib。
ByteBuddy 值得有自己的解释。它像 Javassist 一样易于使用,但似乎缺乏一些使 Javassist 变得出色的功能,例如从头开始创建方法,因此您显然需要使用 ASM。如果您需要对类进行一些轻量级修改,ByteBuddy 是不错的选择,但如果要在保持高级抽象的同时对类进行更高级的修改,Javassist 是更好的选择。
注意:如果我错过了图书馆,请编辑此答案或在评论中提及。
Analysis of bytecode libraries
As I can tell from the answers you got here and ones in the questions that you have looked at, these answers do not formally address the question in the explicit manner you have stated. You asked for a comparison, meanwhile these answers have vaguely stated what one might want based on what your target is (e.g. Do you need to know bytecode? [y/n]), or are too narrow.
This answer is a short analysis of each bytecode framework, and provides a quick comparison at the end.
Javassist
I personally prefer Javassist simply because of how quickly you can get to using it and building and manipulating classes with it. The tutorial is straightforward and easy to follow. The jar file is a tiny 707KB, so it is nice and portable; makes it suitable for standalone applications.
ASM
ASM by ObjectWeb is a very comprehensive library which lacks nothing related to building, generating, and loading classes. In fact, it even has class analysis tools with predefined analyzers. It is said to be the industry standard for bytecode manipulation. It is also the reason why I steer clear away from it.
When I see examples of ASM, it seems like a cumbersome beast of a task with the number of lines it takes to modify or load a class. Even some of the parameters to some methods seem a bit cryptic and out of place for Java. With things like
ACC_PUBLIC
, and plenty of method calls withnull
everywhere, it honestly does look like it is better suited for a low-level language like C. Why not simply just pass a String literal like "public", or an enumModifier.PUBLIC
? It's more friendly and easy to use. That is my opinion, however.For reference, here is an ASM (4.0) tutorial: https://www.javacodegeeks.com/2012/02/manipulating-java-class-files-with-asm.html
BCEL
From what I have seen, this library is your basic class library that lets you do everything you need to—if you can spare a few months or years.
Here is a BCEL tutorial that really spells it out: http://www.geekyarticles.com/2011/08/manipulating-java-class-files-with-bcel.html?m=1
cglib
Despite the fact that you can read information from classes, and that you can transform classes, the library seems tailored to proxies. The tutorial is all about beans for the proxies, and it even mentions it is used by "data access frameworks to generate dynamic proxy objects and intercept field access." Still, I see no reason why you can't use it for the more simple purpose of bytecode manipulation instead of proxies.
ByteBuddy
Long story short, where BCEL is lacking, ByteBuddy is abundant. It uses a primary class called ByteBuddy using the Service Design Pattern. You create a new instance of ByteBuddy, and this represents a class that you want to modify. When you are done with your modifications, you can then make a
DynamicType
withmake()
.On their website is a full tutorial with API documentation. The purpose seems to be for rather high-level modifications. When it comes to methods, there does not appear to be anything in the official tutorial, or any 3rd party tutorial, about creating a method from scratch, apart from delegating a method (EDITME if you know where this is explained).
Their tutorial can be found here on their website. Some examples can be found here.
Java Class Assistant (jCLA)
I have my own bytecode library that I am building, which will be called Java Class Assistant, or jCLA for short, because of another project I am working on and because of said quirks with Javassist
, but I will not be releasing it to GitHub until it is finishedbut the project is currently available to browse on GitHub and give feedback on as it is currently in alpha, but still workable enough to be a basic class library (currently working on the compilers; please help me if you can! It will be released a lot sooner!).It will be quite bare bones with the ability to read and write class files to and from a JAR file, as well as the ability to compile and decompile bytecode to and from source code and class files.
The overall usage pattern makes it rather easy to work with jCLA,
though it may take some getting used toand is apparently quite similar to ByteBuddy in its style of methods and method parameters for class modifications:(VariableInitializer production specification for your convenience.)
As may be implied from the above snippet, each
ClassDefinition
is immutable. This makes jCLA more secure, thread-safe, network-safe, and easy to use. The system revolves primarily around ClassDefinitions as the object of choice for querying information about a class in a high-level manner, and the system is built in such a way that ClassDefinition is converted to and from target types such as ClassBuilder and ClassFile.jCLA uses a tiered system for class data. At the bottom, you have the immutable
ClassFile
: a struct or software representation of a class file. Then you have immutableClassDefinition
s which are converted from ClassFiles into something less cryptic and more manageable and useful to the programmer who is modifying or reading data from the class, and is comparable to information accessed throughjava.lang.Class
. Finally, you have mutableClassBuilder
s. The ClassBuilder is how classes are modified or created. It allows that you can create aClassDefinition
directly from the builder from its current state. Creating a new builder for each class is not necessary as thereset()
method will clear the variables.(Analysis of this library will be available as soon as it is ready for release.)
But until then, as of today:
I still recommend learning about java bytecode however. It will make debugging easier.
Comparison
Considering all of these analyses (excluding jCLA for now), the broadest framework is ASM, the easiest to use is Javassist, the most basic implementation is BCEL, and the most high-level for bytecode generation and proxies is cglib.
ByteBuddy deserves its own explanation. It is easy to use like Javassist, but appears to be lacking some of the features that make Javassist great, such as method creation from scratch, so you would need to use ASM for that apparently. If you need to do some lightweight modification with classes, ByteBuddy is the way to go, but for more advanced modification of classes while maintaining a high level of abstraction, Javassist is a better choice.
Note: if I missed a Library, please edit this answer or mention it in a comment.
如果您对字节码生成的兴趣只是使用它,那么比较图就变得相当简单:
您需要了解字节码吗?
对于 javassist :否
对于所有其他人:是
当然,即使使用 javassist,您也可能在某些时候遇到字节码概念。同样,其他一些库(例如 ASM)具有更高级的 API 和/或工具支持,可以让您免受许多字节码详细信息的影响。
javassist 的真正区别在于它包含一个基本的 java 编译器。这使得编写复杂的类转换变得非常容易:您只需将 java 片段放入 String 中,并使用该库将其插入到程序中的特定点。包含的编译器将构建等效的字节码,然后将其插入到现有的类中。
If your interest in bytecode generation is only to use it, the comparison chart becomes rather simple :
Do you need to understand bytecode?
for javassist : no
for all others : yes
Of course, even with javassist you may be confronted with bytecode concepts at some point. Likewise, some of the other libraries (such as ASM) have a more high-level api and/or tool support to shield you from many of the bytecode details.
What really distinguishes javassist though, is the inclusion of a basic java compiler. This makes it very easy to write complex class transformations : you only have to put a java fragment in a String and use the library to insert it at specific points in the program. The included compiler will build the equivalent bytecode, which is then inserted into the existing classes.
首先,这完全取决于您的任务。您想要生成新代码还是分析现有字节码以及您可能需要多复杂的分析。还有你想投入多少时间来学习 Java 字节码。您可以将字节码框架分解为提供高级 API 的框架,这样当您需要了解 JVM 或使用一些字节码时,您就可以摆脱学习低级操作码和 JVM 内部结构(例如 javaassist 和 CGLIB)和低级框架的麻烦。生成工具(ASM 和 BCEL)。对于分析,BCEL 历史上发展得更多一些,但 ASM 提供了易于扩展的不错的功能。另请注意,ASM 可能是唯一为 Java 7 中默认启用的新字节码验证器所需的 STACK_MAP 信息提供最高级支持的框架。
First of all it all depends on your task. Do you want to generate the new code or analyze existing bytecode and how complex analysis you may need. Also how much time you want to invest into learning Java bytecode. You can break down bytecode framework into ones that provide a high level API, that allows you to get away from learning low level opcodes and JVM internals (e.g, javaassist and CGLIB) and low level frameworks when you need to understand JVM or use some bytecode generation tools (ASM and BCEL). For analyzis BCEL historically evolved a bit more, but ASM provides a decent functionality that is easy to extend. Also note, ASM is probably the only framework that provides the most advanced support for STACK_MAP information required by the new bytecode verifier enabled by default in Java 7.