groovy——运行方式、基本语法、引入方式、metaClass
jvm运行groovy类有两种方式:
1.使用groovyc编译所有的*.groovy为java的*.class文件,把这些*.class文件放在java类路径中,通过java类加载器来加载这些类。
2.通过groovy的类加载器在运行时直接加载*.groovy文件并且生成对象,在这种方式下,没有生成任何*.class,但是生成了一个java.lang.Class对象的实例,也就是说,当groovy代码中包括一个new MyClass()的表达式时,并且也有一个MyClass.groovy的文件,这个文件将被解释,一个MyClass的类型将被产生并且增加到类加载器中,在代码中将像从*.class一样获取到MyClass对象。
没有生成.class文件也可以用生成class对象的实例?
没错,实际上只要有符合虚拟机规范的二进制字节流被解析成方法区的数据结构就可以生成class对象,具体可以看一下类的加载过程。
https://blog.csdn.net/iteye_7617/article/details/82474191
https://www.cnblogs.com/amosli/p/3970810.html
groovy基本语法:
https://www.cnblogs.com/amosli/p/3970810.html
引入方式:
https://www.jianshu.com/p/e8dec95c4326
1.GroovyClassLoader:运行时加载groovy代码,生成.class对象
GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); String scriptText = "class Hello { void hello() { println 'hello' } }"; //将Groovy脚本解析为Class对象 Class clazz = groovyClassLoader.parseClass(scriptText); //Class clazz = groovyClassLoader.parseClass(new File("script.groovy")); assertEquals(clazz.getName(),"Hello"); clazz.getMethod("hello").invoke(clazz.newInstance());
2.GroovyScriptEngine:运行时加载脚本,并监听脚本变化,当脚本发生变化时重新加载,这个厉害了啊
groovy.util.GroovyScriptEngine 类为 GroovyClassLoader 其上再增添一个能够处理脚本依赖及重新加载的功能层, GroovyScriptEngine可以从指定的位置(文件系统,URL,数据库,等等)加载Groovy脚本
可以使用一个CLASSPATH集合(url或者路径名称)初始化GroovyScriptEngine,之后便可以让它根据要求去执行这些路径中的Groovy脚本了.GroovyScriptEngine同样可以跟踪相互依赖的脚本,如果其中一个被依赖的脚本发生变更,则整个脚本树都会被重新编译和加载。
//script/groovy/hello.groovy println "hello $name"
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("script/groovy"); Binding binding = new Binding(); binding.setVariable("name", "groovy"); while (true){ scriptEngine.run("hello.groovy", binding); TimeUnit.SECONDS.sleep(1); }
3.GroovyShell:这种看起来不是很实用...
GroovyShell shell = new GroovyShell(); //可以绑定更多变量 shell.setVariable("age",22); //直接求值 shell.evaluate("if(age < 18){'未成年'}else{'成年'}"); //解析为脚本,延迟执行或者缓存起来 Script script = shell.parse("if(age < 18){'未成年'}else{'成年'}"); assertEquals(script.run(), "成年"); //可以从更多位置加载/执行脚本 //shell.evaluate(new File("script.groovy")); //shell.evaluate(new URI("http://wwww.a.com/script.groovy"));
还可以这样:
GroovyShell shell = new GroovyShell(); Script scrpt = shell.parse("script.groovy"); Binding binding = new Binding(); binding.setVariable("str1", "value1"); binding.setVariable("str2", "value2"); scrpt.setBinding(binding); System.out.println(scrpt.evaluate("customConcat(str1, str2)")); script.groovy: def customConcat(def str1, def str2) { str1.concat(str2) }
4.Eval:这种是对GroovyShell的封装
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding);
Script scrpt = shell.parse("script.groovy");
binding.setVariable("str1", "value1");
binding.setVariable("str2", "value2");
binding.setVariable("newConcat", scrpt);
System.out.println(scrpt.evaluate("newConcat.customConcat(str1, str2)"));
metaClass:
运行期添加静态方法和普通方法
https://blog.csdn.net/walkcode/article/details/24587457
groovy代码和class文件的对应关系:
https://blog.csdn.net/jiangqian6481/article/details/83717442
1.对于没有任何类定义
如果Groovy脚本文件里只有执行代码,没有定义任何类(class),则编译器会生成一个Script的子类,类名和脚本文件的文件名一样,而脚本的代码会被包含在一个名为run的方法中,同时还会生成一个main方法,作为整个脚本的入口。
2.对于仅有一个类
如果Groovy脚本文件里仅含有一个类,而这个类的名字又和脚本文件的名字一致,这种情况下就和Java是一样的,即生成与所定义的类一致的class文件, Groovy类都会实现groovy.lang.GroovyObject接口。
3.对于多个类
如果Groovy脚本文件含有一个或多个类,groovy编译器会很乐意地为每个类生成一个对应的class文件。如果想直接执行这个脚本,则脚本里的第一个类必须有一个static的main方法。
4.对于有定义类的脚本
如果Groovy脚本文件有执行代码, 并且有定义类, 那么所定义的类会生成对应的class文件, 同时, 脚本本身也会被编译成一个Script的子类,类名和脚本文件的文件名一样
如何使用groovyc 和 groovy: