Вы должны знать о нескольких вещах.Когда вы создаете скрипт (назовем его someScript.groovy
) со следующим содержимым:
#!groovy
println "Test"
println 21 + 21
он компилируется в следующий класс:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
public class someScript extends Script {
public someScript() {
}
public someScript(Binding context) {
super(context);
}
public static void main(String... args) {
InvokerHelper.runScript(someScript.class, args);
}
public Object run() {
((someScript)this).println("Test");
Object var10000 = null;
((someScript)this).println(21 + 21);
return null;
}
}
Как видите,Тело скрипта Groovy представлено в виде метода run()
в сгенерированном классе.Когда мы добавим класс в этот сценарий, скажем, класс Example
из вашего вопроса, тело метода run()
не изменится вообще - класс будет скомпилирован в файл байт-кода Example.class
и все:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Closure;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.GeneratedClosure;
public class Example implements GroovyObject {
public Example() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static void main(String... args) {
class _main_closure1 extends Closure implements GeneratedClosure {
public _main_closure1(Object _outerInstance, Object _thisObject) {
super(_outerInstance, _thisObject);
}
public Object doCall(Object it) {
DefaultGroovyMethods.println(Example.class, "Hello World");
return null;
}
public Object call(Object args) {
return this.doCall(args);
}
public Object call() {
return this.doCall((Object)null);
}
public Object doCall() {
return this.doCall((Object)null);
}
}
Closure clos = new _main_closure1(Example.class, Example.class);
clos.call();
}
}
Когда мы запустим Groovy-компилятор для компиляции someScript.groovy
(groovyc someScript.groovy
) и перечислим сгенерированные классы, мы увидим что-то вроде этого:
ls -lh *.class
-rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:26 Example.class
-rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:26 'Example$_main_closure1.class'
-rw-rw-r--. 1 wololock wololock 1,4K 12-07 10:26 someScript.class
ПРИМЕЧАНИЕ: это Example$_main_closure1.class
представляет замыкание, используемое в Example.main()
методе
Теперь давайте посмотрим, что произойдет, если мы закомментируем (или удалим) операторы println
из файла someScript.groovy
имы компилируем его:
someScript.groovy
#!groovy
class Example {
static void main(String[] args) {
def clos = {println "Hello World"};
clos.call();
}
}
//println "Test"
//
//println 21 + 21
Время компиляции:
> groovyc someScript.groovy
> ls -lh *.class
-rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:31 Example.class
-rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:31 'Example$_main_closure1.class'
Как видите, * 1040 нет* файл класса создан.Это происходит потому, что файл скрипта, который мы только что скомпилировали, не содержит никакого тела, но внутри него есть класс Example
.Когда вы запускаете такой скрипт, Groovy пытается найти первый статический метод main()
для его выполнения - поэтому запуск следующего скрипта приводит к выводу Hello World
:
> groovy someScript.groovy
Hello World
Давайте пойдем дальше и добавим еще один класс дляначало файла someScript.groovy
:
someScript.groovy
#!groovy
class Foo {
static void main(String[] args) {
println "Bar"
}
}
class Example {
static void main(String[] args) {
def clos = {println "Hello World"};
clos.call();
}
}
//println "Test"
//
//println 21 + 21
Тело сценария все еще закомментировано.Давайте скомпилируем и посмотрим, какие файлы классов генерируются:
> groovyc someScript.groovy
> ls -lh *.class
-rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:35 Example.class
-rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:35 'Example$_main_closure1.class'
-rw-rw-r--. 1 wololock wololock 1,8K 12-07 10:35 Foo.class
Мы можем видеть 3 файла классов, как и ожидалось.Давайте посмотрим, что произойдет, если мы запустим скрипт с командой groovy
:
> groovy someScript.groovy
Bar
Теперь, как вы можете видеть, метод Foo.main()
был запущен, потому что Groovy разместил этот метод поверх файла скрипта и предположил, чточто это основной метод, который мы хотим запустить.
Давайте завершим это с примером, содержащим два класса и тело скрипта:
someScript.groovy
#!groovy
class Foo {
static void main(String[] args) {
println "Bar"
}
}
class Example {
static void main(String[] args) {
def clos = {println "Hello World"};
clos.call();
}
}
println "Test"
println 21 + 21
Время компиляции:
> groovyc someScript.groovy
> ls -lh *.class
-rw-rw-r--. 1 wololock wololock 2,0K 12-07 10:39 Example.class
-rw-rw-r--. 1 wololock wololock 1,6K 12-07 10:39 'Example$_main_closure1.class'
-rw-rw-r--. 1 wololock wololock 1,8K 12-07 10:39 Foo.class
-rw-rw-r--. 1 wololock wololock 1,4K 12-07 10:39 someScript.class
На этот раз класс someScript
сгенерирован, потому что тело скрипта не пустое.Последний взгляд на сгенерированный файл someScript.class
:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
public class someScript extends Script {
public someScript() {
}
public someScript(Binding context) {
super(context);
}
public static void main(String... args) {
InvokerHelper.runScript(someScript.class, args);
}
public Object run() {
((someScript)this).println("Test");
Object var10000 = null;
((someScript)this).println(21 + 21);
return null;
}
}
Как вы можете видеть, он не изменился по сравнению с нашим первым примером (когда в скрипте не было классов, а было только два оператора println),поэтому мы не можем ожидать ничего другого, кроме запуска метода someScript.run()
.Давайте запустим скрипт:
> groovy someScript.groovy
Test
42
Заключение
- Когда вы создаете скрипт Groovy, его тело перемещается и компилируется как метод
scriptName.run()
, и он выполняется. - Если вы добавляете класс с методом
main()
в скрипт Groovy и сохраняете тело скрипта, добавленный метод класса main()
не будет выполняться - он только компилирует класс, и вы можете использовать его явно в вашемтело скрипта, если необходимо. - Если вы добавляете класс с методом
main()
в скрипт Groovy и не помещаете тело скрипта (любые операторы / выражения вне класса), тогда Groovy ищет первыйстатический main()
метод, и он выполняет его.