首先需要了解ByteBuddy是什么,ByteBuddy是一款java字节码增强框架,可以动态的生成java字节码文件,比起我们自己进行字节码文件的生成,它屏蔽了底层细节,提供一套统一易上手的Api,简化了字节码增强的学习难度。
为什么需要字节码增强技术?ByteBuddy官方文档已经给出了答案
The Java language comes with a comparatively strict type system. Java requires all variables and objects to be of a specific type and any attempt to assign incompatible types always causes an error. These errors are usually emitted by the Java compiler or at the very least by the Java runtime when casting a type illegally. Such strict typing is often desirable, for example when writing business applications. Business domains can usually be described in such an explicit manner where any domain item represents its own type. This way, we can use Java to build very readable and robust applications where mistakes are caught close to their source. Among other things, it is Java’s type system that is responsible for Java’s popularity in enterprise programming.
=
However, by enforcing its strict type system, Java imposes limitations that restrict the language’s scope in other domains. For example, when writing a general-purpose library that is to be used by other Java applications, we are normally not able to reference any type that is defined in the user’s application because these types are unknown to us when our library is compiled. In order to call methods or to access fields of the user’s unknown code, the Java Class Library comes with a reflection API. Using the reflection API, we are able to introspect unknown types and to call methods or access fields. Unfortunately, the use of the reflection API has two significant downsides:
Using the reflection API is slower than a hard-coded method invocation: First, one needs to perform a rather expensive method lookup to get hold of an object that describes a specific method. And when a method is invoked, this requires the JVM to run native code which requires long run time compared to a direct invocation. However, modern JVMs know a concept called inflation where the JNI-based method invocation is replaced by generated byte code that is injected into a dynamically created class. (Even the JVM itself uses code generation!) After all, Java’s inflation system remains with the drawback of generating very general code that for example only works with boxed primitive types such that the performance drawback is not entirely settled.
The reflection API defeats type-safety: Even though the JVM is capable of invoking code by reflection, the reflection API is itself not type-safe. When writing a library, this is not a problem as long as we do not need to expose the reflection API to the library’s user. After all, we do not know the user code during compilation and could not validate our library code against its types. Sometimes, it is however required to expose the reflection API to a user by for example letting a library invoke one of our own methods for us. This is where using the reflection API becomes problematic as the Java compiler would have all the information to validate our program’s type safety. For example, when implementing a library for method-level security, a user of this library would want the library to invoke a method only after enforcing a security constraint. For this, the library would need to reflectively call a method after the user handed over the required arguments for this method. Doing so, there is however no longer a compile-time type check if these method arguments match with the method’s reflective invocation. The method call is still validated but the check is delayed until runtime. Doing so, we voided a great feature of the Java programming language.
This is where runtime code generation can help us out. It allows us to emulate some features that are normally only accessible when programming in a dynamic languages without discarding Java’s static type checks. This way, we can get the best of both worlds and additionally improve runtime performance. To get a better understanding of this problem, let us look at the example of implementing the mentioned method-level security library.
简单总结就是java的反射存在诸多限制,java开发者需要一种手段模拟一些动态语言才具有的特性,而且不失去自己安全类型的特性,相比cglib,javasist等相同功能的工具,bytebuddy 更容易上手且具有更高的性能。
本篇博客将根据官方文档,介绍bytebuddy的一些功能和特性的使用,也作为笔者自己的一个学习记录。
本篇博客所有的代码示例都基于bytebuddy的1.8.0版本