字节码技术在我们常见的各大框架中都有用到. 这篇文章我们将讲解 ASM 在 cglib 和 fastjson 上的实际使用案例。
cglib 的简单应用
如果说 ASM 是字节码改写事实上的标准,那么可以说 cglib 则是动态代理事实上的标准。cglib 是一个强大的、高性能的代码生成库,被大量框架使用
Spring:为基于代理的 AOP 框架提供方法拦截
MyBatis:用来生成 Mapper 接口的动态代理实现类
Hibernate:用来生成持久化相关的类
Guice、EasyMock、jMock 等在实现内部,cglib 库使用了 ASM 字节码操作框架来转化字节码,产生新类,帮助开发者屏蔽了很多字节码相关的内部细节,不用再去关心类文件格式、指令集等
有这样一个 Person 类,想在 doJob 调用前和调用后分别记录一些日志
我们可以使用 JDK 动态代理来实现,不过介于 JDK 动态代理有个明显的缺点(需要目标对象实现一个或多个接口),在这里重点介绍 cglib 的实现方案。一个典型的实现方案是实现一个 接口,用来拦截方法调用。这个接口只有一个方法:
这个方法的第一个参数 obj 是代理对象,第二个参数 method 是拦截的方法,第三个参数是方法的参数,第四个参数 proxy 用来调用父类的方法。MethodInterceptor 作为一个桥梁连接了目标对象和代理对象
cglib 代理的核心是 类,它用于创建一个 cglib 代理。这个类有一个静态方法,该方法的第一个参数 type 指明要代理的对象类型,第二个参数 callback 是要拦截的具体实现,一般都会传入一个 MethodInterceptor 的实现
运行上面的代码输出:
可以用设置系统变量让 cglib 输出生成的文件
核心类是 ,这个类的反编译以后的代码如下
可以看到 cglib 生成了一个 Person 的子类,实现了 doJob 方法,此方法会调用 MethodInterceptor 的 intercept 函数,这个函数先输出 ">>>>>before intercept" 然后调用父类(也即真正的 Person 类)的 doJob 的方法,最后输出
FastJson
fastjson 是目前 java 语言中最快的 json 库,比自称最快的 jackson 速度要快。fastjson 库内置 ASM,基于 objectweb asm 3.3 改造,只保留必要的部分不到 2000 行代码,通过 ASM 自动生成序列号、反序列化字节码,减少反射开销,理论上可以提高 20% 的性能。如果不用反射,一个 json 反序列化要怎么样来做呢?下面写了一个最简单粗暴的例子,来反序列化下面的 json 字符串
假定不考虑嵌套,特殊字符的情况,不做语法解析的情况下,可以这么来写
可以看到获取获取字段 field、设置字段值都需要通过反射的方式。那么 fastjson 是怎么解决反射低效的问题的呢?通过调试的方式,把 fastjson 生成的字节码写入到文件中。针对 MyBean,fastjson 使用 ASM 为它生成了一个反序列化的类,里面硬编码了处理序列化需要用到的所有可能场景,不再需要任何反射相关的代码。结合创新的 sort field fast match 算法,速度更上一层楼。下面是通过阅读字节码精简以后的 Java 代码。
通过上面的两个例子,我们可以看到 ASM 字节码技术在底层库上的强大。可能每天写业务代码不会需要使用这些底层的优化,但是当我们想造一个轮子,想读懂开源代码背后的核心时,都不得不深入的去学习了解这部分知识,很难,但很值得。
小结
这篇文章我们主要讲解了 ASM 字节码改写技术在 cglib 和 fastjson 上的应用,一起来回顾一下要点:
第一,cglib 使用 ASM 生成了目标代理类的一个子类,在子类中扩展父类方法,达到代理的功能,因此要求代理的类不能是 final 的。
第二,fastjson 使用 ASM 生成了实例 Bean 反序列化类,彻底去掉了反射的开销,使性能更上一层楼。
领取专属 10元无门槛券
私享最新 技术干货