虽然java在运行时理解编译后的groovy类没有任何问题,但是它不能懂得.groovy 的源文件,如果要在运行时动态加载“.groovy”文件,groovy需要在后台做更多的工作。
需要了解java相关的高级知识,如果一点都不了解类加载器,那么跳过这个主题,假定groovy源代码被正确的转换为java字节码。不需要完全懂得到底发生了什么,不至于为学习groovy而失眠。
Groovy的语法是面向行的,但是执行groovy代码确不是这样的,不像别的脚本语言,groovy代码不是一行一行的解释执行的。
相反,groovy代码被完整的转换,通过转换器产生一个java类,产生的这个类是groovy和java之间的粘合剂。产生的groovy类的格式与java字节码的格式是一样的。 在java运行的时候,类通过类加载器进行管理,当请求一个java类加载器加载一个类的时候,java类加载器从*.class文件中加载这个类,存储在缓存中,然后返回这个类。
由于一个groovy产生的类也是一个java类,所以这个类也可以通过类加载器的相同行为进行管理。差别在于groovy的类加载器能够从*.groovy文件加载类(在放入缓存之前进行转换和生成类)。
Groovy在运行时可以像读取*.class文件一样读取*.groovy文件,在运行的groovyc编译器产生了相应的类,编译器使用转换和产生类的相同的机制简单的把*.groovy文件转换为*.class文件。
在运行时生成groovy类
假设有一个groovy脚本,这个脚本保存在MyScript.groovy文件中,通过命令groovy MyScript.groovy 来运行这个脚本,下面是这个类产生的步骤,就像图2.7显示的那样:
1、 MyScript.groovy被传递给groovy的转换器;
2、 转换器产生一个抽象语法树(AST)来表示在MyScript.groovy中的所有代码;
3、 Groovy类生成器根据AST产生java字节码,根据脚本的内容,结果可能是多个类,现在类通过groovy类加载器是可以使用的了;
4、 Java运行时像调用一个java类MyScript一样来调用第三部产生的类;
图2.8显示了第二种途径,当用groovyc代替groovy时,产生的类被写到*.class文件中,这两种方式使用同样的类生成机制。
所有的这些工作都在后台进行,以至于你会感觉groovy像解释执行语言。但是实际上它不是,在使用之前类已经完整的构建并且在运行时不能进行更改(不排除在.groovy被修改之后对类在运行时的替换)。
图2.8 在运行环境或者编译到一个class文件的时候,groovy字节码生成流程根据这个描述,你能恰当的了解到groovy是怎样的动态语言,所有groovy代码都是静态java类格式,正如你所见,groovy通过聪明的途径来执行类的构建和方法调用。