Java 程序员几乎都了解 Spring。它的 IoC(依赖反转)和 AOP(面向切面编程)功能非常强大、易用。而它背后的字节码生成技术(在运行时,根据需要修改和生成 Java 字节码的技术)就是就是一项重要的支撑技术。
Java 字节码能够在 JVM(Java 虚拟机)上解释执行,或即时编译执行。其实,除了Java,JVM 上的 Groovy、Kotlin、Closure、Scala 等很多语言,也都需要生成字节码。另外,playscript 也可以生成字节码,从而在 JVM 上高效地运行!
而且,字节码生成技术很有用。你可以用它将高级语言编译成字节码,还可以向原来的代码中注入新代码,来实现对性能的监测等功能。
目前,我就有一个实际项目的需求。我们的一个产品,需要一个规则引擎,解析自定义的DSL,进行规则的计算。这个规则引擎处理的数据量比较大,所以它的性能越高越好。因此,如果把 DSL 编译成字节码就最理想了。
既然字节码生成技术有很强的实用价值,那么本文就带你掌握它。
我会先带你了解 Java 的虚拟机和字节码的指令,然后借助 asm 这个工具,生成字节码,最后,再实现从 AST 编译成字节码。通过这样一个过程,你会加深对 Java 虚拟机的了解,掌握字节码生成技术,从而更加了解 Spring 的运行机制,甚至有能力编写这样的工具!
Java虚拟机和字节码
字节码是一种二进制格式的中间代码,它不是物理机器的目标代码,而是运行在 Java 虚拟机上,可以被解释执行和即时编译执行。
在讲后端技术时,我强调的都是,如何生成直接在计算机上运行的二进制代码,这比较符合C、C++、Go 等静态编译型语言。但如果想要解释执行,除了直接解释执行 AST 以外,我没有讲其他解释执行技术。
而目前更常见的解释执行的语言,是采用虚拟机,其中最典型的就是 JVM,它能够解释执行 Java 字节码。
而虚拟机的设计又有两种技术:一是基于栈的虚拟机;二是基于寄存器的虚拟机。
标准的 JVM 是基于栈的虚拟机(后面简称“栈机”)。
每一个线程都有一个 JVM 栈,每次调用一个方法都会生成一个栈桢,来支持这个方法的运行。栈桢里面又包含了本地变量数组(包括方法的参数和本地变量)、操作数栈和这个方法所用到的常数。
栈机是基于操作数栈做计算的。以“2+3”的计算为例,只要把它转化成逆波兰表达式,“2 3 +”,然后按照顺序执行就可以了。也就是:先把 2 入栈,再把 3 入栈,再执行加法指令,这时,要从栈里弹出 2 个操作数做加法计算,再把结果压入栈。