Groovy入門


 

參照:https://juejin.cn/post/6954350461818421278#heading-1

1. 編寫 Groovy 邏輯的兩種風格

  • 腳本(不定義和.groovy文件同名的class)
  • 類(定義class,所有代碼都在class里)

  在 .groovy 文件內,可以不聲明任何類而直接在文件頂級層次編寫代碼邏輯 (筆者剛才就是這樣做的)。不過這樣的話,就不能在文件的頂級層次再聲明一個和文件同名的類,否則編譯器會給出 there is a synthetic class generated for script code 的錯誤。從編譯角度來看這可以理解,因為 .groovy 文件被編譯成 .class 文件並執行時,編譯器實際上會為其生成一個合成類,而正是這一步導致了沖突發生:我們剛定義的類名和它重復了。  

  實際上,如果 .groovy 文件內部出現了和文件同名的類,則意味着這個 .groovy 文件會被視作是一段 "用 Groovy 方言編寫的 Java 代碼",一般它也就不再作為腳本使用,而是變成一個 "普通的類" ( IDEA 稱它是一個 Groovy Class) 。這么做的一個直接后果是,我們不能夠在文件的頂級層次直接編寫代碼邏輯。

 

2. Java集成groovy之GroovyShell、GroovyScriptEngine、GroovyClassLoader

 https://www.cnblogs.com/jsersudo/p/10178407.html

  • GroovyClassLoader

用 Groovy 的 GroovyClassLoader ,動態地加載一個腳本並執行它的行為。GroovyClassLoader是一個定制的類裝載器,
負責解釋加載Java類中用到的Groovy類。

  • GroovyShell

GroovyShell允許在Java類中(甚至Groovy類)求任意Groovy表達式的值。您可使用Binding對象輸入參數給表達式
並最終通過GroovyShell返回Groovy表達式的計算結果。

  • GroovyScriptEngine

GroovyShell多用於推求對立的腳本或表達式,如果換成相互關聯的多個腳本,使用GroovyScriptEngine會更好些。
GroovyScriptEngine從您指定的位置(文件系統,URL,數據庫,等等)加載Groovy腳本,並且隨着腳本變化而重新加載它們。
如同GroovyShell一樣,GroovyScriptEngine也允許您傳入參數值,並能返回腳本的值

 

 1 package groovy;
 2 
 3 import groovy.lang.*;
 4 import groovy.util.GroovyScriptEngine;
 5 import groovy.util.ResourceException;
 6 import groovy.util.ScriptException;
 7 
 8 import java.io.File;
 9 import java.io.IOException;
10 
11 public class GroovyIntoJavaDemo1 {
12     //測試次數
13     private static final int num = 10000;
14 
15     public static void main(String[] args) throws IOException, ResourceException, ScriptException {
16 
17         /*
18         GroovyClassLoader
19          */
20         GroovyClassLoader loader =  new GroovyClassLoader();
21         Class aClass = loader.parseClass(new File("src/main/java/groovy/CycleDemo.groovy"));
22         try {
23             GroovyObject instance = (GroovyObject) aClass.newInstance();
24             instance.invokeMethod("cycle", new Object[] {"GroovyClassLoader", num});
25 
26         } catch (InstantiationException e) {
27             e.printStackTrace();
28         } catch (IllegalAccessException e) {
29             e.printStackTrace();
30         }
31 
32 
33         /*
34         GroovyShell
35          */
36         new GroovyShell().parse( new File( "src/main/java/groovy/CycleDemo.groovy" ) )
37                 .invokeMethod("cycle", new Object[] {"GroovyShell", num});
38 
39         /*
40         GroovyShell,Binding
41          */
42         Binding binding = new Binding();
43         // 傳參數
44         binding.setVariable("scene", "GroovyShell_Binding");
45         binding.setVariable("number", num);
46 
47         GroovyShell groovyShell = new GroovyShell(binding);
48         Script script = groovyShell.parse(new File( "src/main/java/groovy/CycleDemo.groovy" ));
49         binding.setVariable("cycleDemo", script);  // 傳腳本實例
50 
51         script.evaluate("cycleDemo.cycle(scene, number)");  // in_cycle, number=10000
52 
53         groovyShell.evaluate(new File( "src/main/java/groovy/CycleDemo.groovy" ));  // out_of_cycle,無法調用到方法
54 
55 
56         /*
57         GroovyScriptEngine
58          */
59         Class script1 = new GroovyScriptEngine("src/main/java/groovy/")
60                 .loadScriptByName("CycleDemo.groovy");
61         try {
62             Script instance =(Script) script1.newInstance();
63             instance.invokeMethod ("cycle",new Object[]{"GroovyScriptEngine", num});
64         } catch (InstantiationException e) {
65             e.printStackTrace();
66         } catch (IllegalAccessException e) {
67             e.printStackTrace();
68         }
69     }
70 }

執行結果:

1 [GroovyClassLoader]in_cycle, number=10000
2 [GroovyShell]in_cycle, number=10000
3 [GroovyShell_Binding]in_cycle, number=10000
4 out_of_cycle
5 [GroovyScriptEngine]in_cycle, number=10000

 

2.1 evaluate

Script:
  Object evaluate(String expression) // 參數為groovy表達式
GroovyShell:
  public Object evaluate(final String scriptText) // 參數為groovy腳本

https://blog.csdn.net/jiangtao_st/article/details/19496989

 

 1 package groovy;
 2 
 3 import groovy.lang.Binding;
 4 import groovy.lang.GroovyShell;
 5 import groovy.lang.Script;
 6 
 7 public class TestScript {
 8     public static void main(String[] args) {
 9 
10         GroovyShell groovyShell = new GroovyShell();
11         /**
12          * 腳本為
13          def customConcat(def str1, def str2) {
14             str1.concat(str2)
15          }
16          */
17         Script scrpt = groovyShell.parse("\n" +
18                 "def customConcat(def str1, def str2) {\n" +
19                 "str1.concat(str2)\n" +
20                 "}");
21 
22         Binding binding = new Binding();
23         binding.setVariable("str1", "value1");
24         binding.setVariable("str2", "value2");
25         // binding.setVariable("newConcat", scrpt);
26 
27         scrpt.setBinding(binding);
28         System.out.println(scrpt.evaluate("str1.concat(str2)"));
29 
30         // 會拋異常
31         // Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Script1.customConcat() is applicable for argument types: (java.lang.String, java.lang.String) values: [value1, value2]
32         //    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
33         //    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:81)
34         //    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
35         //    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
36         //    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:174)
37         //    at Script1.run(Script1.groovy:1)
38         System.out.println(scrpt.evaluate("customConcat(str1, str2)"));  // 會拋異常
39 
40         // 這種調用evaluate()執行腳本方法行不通是因沒有腳本里定義的方法沒有綁定,因此可以把script綁定給binding,然后執行的binding的引用方法:
41         System.out.println(scrpt.evaluate("newConcat.customConcat(str1, str2)"));    // 解開第25行注釋就並且改為這樣子,就不會拋異常
42     }
43 }

 

2.2 通過Binding傳遞Java對象,groovy里面又可以調用該對象的方法

 1 package groovy;
 2 
 3 import groovy.lang.Binding;
 4 import groovy.lang.GroovyShell;
 5 import org.springframework.core.io.Resource;
 6 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 7 import org.springframework.core.io.support.ResourcePatternResolver;
 8 
 9 import java.io.File;
10 import java.io.FileInputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 
14 public class GroovyShellEvaluate {
15 
16     private String getContent(String classpathFile) {
17         StringBuilder content = new StringBuilder();
18         ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
19         Resource resource = resourcePatternResolver.getResource(classpathFile);
20         try {
21             File file = resource.getFile();
22             InputStream is = new FileInputStream(file);
23             byte[] c = new byte[1024];
24             int len = 0;
25             while ((len = is.read(c)) != -1) {
26                 String buffer = new String(c, 0, len);
27                 content.append(buffer);
28             }
29         } catch (IOException e) {
30             e.printStackTrace();
31         }
32         return content.toString();
33     }
34 
35     private File getFile(String classpathFile) {
36         File file = null;
37         ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
38         Resource resource = resourcePatternResolver.getResource(classpathFile);
39         try {
40             file = resource.getFile();
41         } catch (IOException e) {
42             e.printStackTrace();
43         }
44         return file;
45     }
46 
47     public static void main(String[] args) throws IOException {
48         Binding binding = new Binding();
49         Table table = new Table();
50         binding.setProperty("tool", "car");
51         binding.setProperty("t", table);   // 傳遞java對象,groovy里面可以調用該對象的方法
52         GroovyShellEvaluate shellEvaluate = new GroovyShellEvaluate();
53         File file = shellEvaluate.getFile("classpath:randomNumberGenerator.groovy");
54         GroovyShell shell = new GroovyShell(binding);
55         shell.evaluate(file);
56 
57         System.out.println("***************content**************");
58         System.out.println(shellEvaluate.getContent("classpath:randomNumberGenerator.groovy"));
59     }
60 
61 }

 

1 package groovy;
2 
3 public class Table {
4 
5     public String generate() {
6         return "[inTable] generate something";
7     }
8 }

 

// randomNumberGenerator.groovy,腳本放在src/main/resources目錄下
def a = "hello"
println "${a}, i want to use ${tool}"

def res = t.generate()   // 調用對象t的generate()方法
println res

class rand {

}

 

結果:

 1 hello, i want to use car
 2 [inTable] generate something 3 ***************content************** 4 def a = "hello" 5 println "${a}, i want to use ${tool}" 6 7 def res = t.generate() 8 println res 9 10 class rand { 11 12 }

 

3. MOP(MetaObject Protocol)& metaClass & 運行時元編程

3.1 運行時元編程

  Groovy 提供兩類元編程,分別為:運行時元編程與編譯時元編程。第一種允許在運行時改變類模型,而第二種發生在編譯時。此處重點講解運行時元編程。(https://groovys.readthedocs.io/zh/latest/GettingStarted/Runtime-and-compile-time-metaprogramming.html#id1

https://blog.csdn.net/Dream_Weave/article/details/106150352

     在運行時元編程中,我們在運行時攔截,注入甚至合成類和接口的方法。為了深入理解 Groovy MOP , 我們需要理解 Groovy 的對象及方法的處理方式。Groovy 中有三類對象:POJO,POGO 和 Groovy 攔截器。Groovy 中對於以上對象均能使用元編程,但使用方式會有差別。

  • POJO - 正規的 Java 對象,其類可以用Java或任何其他 JVM 語言編寫。
  • POGO - Groovy 對象,其類使用 Groovy 編寫。繼承於 java.lang.Object 並且實現 groovy.lang.GroovyObject 接口。
  • Groovy Interceptor - Groovy 對象,實現 groovy.lang.GroovyInterceptable 接口,具有方法攔截能力,我們將在 GroovyInterceptable 章節詳細講解。

  

  如下例子參考:https://segmentfault.com/a/1190000021090823

  在Groovy中任何對象都實現GroovyObject接口,所以MyMetaTest 也默認實現了GroovyObject接口。如果調用MyMetaTest 中定義了的方法,如:hello,就會直接調用。如果調用MyMetaTest 中未定義方法,如:hello2,如果覆蓋了invokeMethod就會執行invokeMethod方法,否則拋出MissingMethodException異常。

 1 // MyMetaTest.groovy
 2  class MyMetaTest {
 3      def hello() {
 4          return 'invoked hello directly'
 5      }
 6  
 7      @Override
 8      Object invokeMethod(String name, Object args) {
 9          return "invokeMethod: name:${name}, args:${args}"
10     }
11 }
12 
13 MyMetaTest test = new MyMetaTest()
14 println test.hello() // invoked hello directly
15 
16 println test.hello2('kerwin') // invokeMethod: name:hello2, args:[kerwin]

 

  讓MyMetaTest 實現GroovyInterceptable接口(Interceptable —— 可攔截),該接口是一個標記接口,沒有任何方法需要實現。從這個接口的描述可知:實現該接口的類,類中的方法被調用時都會默認使用invokeMethod方法,不管該方法是否已經定義。如果要直接調用已定義的方法,需要使用.&操作符。

 1  class MyMetaTest implements GroovyInterceptable{
 2      def hello() {
 3          return 'invoked hello directly'
 4      }
 5  
 6      @Override
 7      Object invokeMethod(String name, Object args) {
 8          return "invokeMethod: name:${name}, args:${args}"
 9      }
10 }
11 
12 MyMetaTest test = new MyMetaTest()
13 
14 println test.hello() // invokeMethod: name:hello, args:[]
15 
16 println test.hello2('kerwin') // invokeMethod: name:hello2, args:[kerwin]
17 
18 println test.&hello() // invoked hello directly

 

3.2 什么是metaClass

理解:https://juejin.cn/post/6844904032968900621

在Groovy語言中,每個對象都有一個名稱為metaClass的MetaClass類的對象。 此metaClass對象負責保存與該對象有關的所有信息。 每當您對該對象執行任何操作時,Groovy的調度機制都會通過該元類對象(metaClass)路由調用。 因此,如果要更改任何對象/類的行為,則必須更改附加到該類/對象的MetaClass對象,並且它將在運行時更改該類/對象的行為

deletegate 代理的是誰

大白話來說,誰調用就代理誰(實例對象)。

 1 // metaClass1.groovy
 2 Integer.metaClass.isEven = { ->
 3     delegate%2 == 0
 4 }
 5 
 6 int a = 10
 7 int b = 11
 8 
 9 println "a.isEven(): ${a.isEven()}"
10 println "b.isEven(): ${b.isEven()}"

結果:groovy metaClass1.groovy

a.isEven(): true
b.isEven(): false

 

 1 // metaClass2.groovy
 2 Integer.metaClass.isEven = { ->
 3     num%2 == 0
 4 }
 5 
 6 int a = 10
 7 int b = 11
 8 
 9 println "a.isEven(): ${a.isEven()}"
10 println "b.isEven(): ${b.isEven()}"

結果:groovy metaClass2.groovy

Caught: groovy.lang.MissingPropertyException: No such property: num for class: metaClass2
groovy.lang.MissingPropertyException: No such property: num for class: metaClass2
at metaClass2$_run_closure1.doCall(metaClass2.groovy:3)
at metaClass2.run(metaClass2.groovy:9)

 

 1 // metaClassStatic.groovy
 2 Integer.metaClass.static.isEven = { num ->
 3     num%2 == 0
 4 }
 5 
 6 int a = 10
 7 int b = 11
 8 
 9 println Integer.isEven(1)
10 println "b.isEven(2): ${b.isEven(2)}"

結果:groovy metaClassStatic.groovy

false
b.isEven(2): true

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM