zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    python socket练习
    python异常处理
    python类的反射
    类的特殊成员方法
    staticmethod classmethod property方法
    类的多态
    类的析构、继承
    python subprocess模块
    python面向对象
    discuz 使模板中的函数不解析 正常使用
  • 原文地址:https://www.cnblogs.com/wxdlut/p/15589685.html
Copyright © 2011-2022 走看看