[TOC]

概述

Groovy特点

  • 同时支持静态与动态类型
  • 支持运算符重载
  • 对于正则表达式的本地支持
  • 对xml和html的原生支持
  • 与Java无缝衔接

安装

可以参考Groovy官网上的方式,利用SDK来安装Groovy。

基本语法

Helloword

//类似Java的方式
class HelloWorld{
  static void main(String[] args){
      //Using a simple printn statement to print output to console.
    println('hello world);
  }
}
//如果用Groovy脚本的方式:
    println 'hello world'

导入

import groovy.xml.MarkupBuilder
def xml = new MarkupBuilder()

默认情况下,Groovy在代码中包括了一些库,不需要显示的导入。

java.lang.*;
java.util.*;
java.io.*;
java.net.*;

groovy.lang.*;
groovy.util.*;

java.math.BigInteger;
java.math.BigDecimal;

Groovy语句

语句可以是一个关键字,一个标识符,常量,字符串文字或者是符号

注释

参考Java的就可以

分号

Groovy可以不用写分号一行就是一条语句

声明

Groovy声明变量的方式是利用def 关键字。标识符以字母,美元或者下划线开头。

def employee
def student
def conter

字符

浮点数,整数,字符和字符串

def i = 12
def j = 12.5
def a = 'a'
def s1 = "aaa"
def s2 = 'aaaaaaa'

数据类型

内置数据类型

  • byte
  • short
  • int
  • long
  • float
  • double
  • char
  • boolean
  • String

数字类(Number)

  • Byte
  • Short
  • Integer
  • Long
  • Float
  • Double
  • BigInteger
  • BIgDecimal

变量

变量的声明有两种:

一种是类似Java的方式声明,一种是利用def关键字,变量的类型在赋值的时候去确定。

String a = "hello"
def a = 'hello'
//
def a = 1
println(a.getClass())
a = new String("1112234")
println(a.getClass())

//利用def的方式,变量的类型还可以随时动态的切换

运算符

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符

主要介绍一下Groovy比Java多出的一些特性

范围运算符

def range = 0..5
println(range.class)
println(range)

输出

class groovy.lang.IntRange
[0, 1, 2, 3, 4, 5]

流程控制

循环

while

while(condition){
    //execution
}

for

for(;;){
    //excution
}

//for in
for(var in range){
    //
}

//for 语句也可以用于循环访问map,将输出一个键值
def e = ["a":12,"b":31,"c":12,"d":32]
for (a in e){
    println(a)
}

break

终止内层循环

continue

当执行continue语句时,控制立即传递到最近的封闭循环的测试条件,以确定循环是否应该继续。对于该特定循环迭代,循环体中的所有后续语句都将被忽略。

条件

if

if-else

switch

方法

Groovy用def关键字定方法。方法可以接受任意数量参数,可不用显示定义类型。可以添加修饰符,默认的是public的。

def methodName(parm1, param2, param3){
    //code 
}

参数

可以添加任意多个参数,并且可以指定默认参数,但是默认参数必须定义在参数列表的末尾。

def someMethod(param1, param2. param3 = 3){
    //method code 
}

返回值

def add (x, y, z = 3, w = 4){
    x + y + z + w;
}

s = add(1,2)
println(s)

上面的代码,默认的返回最后一行表达式的值。

但是为了避免歧义:我们可以通过这种方式来进行:

def int add(x ,y){  //定义返回值类型。
    x + y
}

//or
def add(x, y){
    return x + y
}
//通过以上两种方式可以明确这个方法是否有返回值,这样避免歧义。

文件I/O

Groovy在使用IO时提供了许多辅助方法。Groovy提供了更简单的类来为文件提供以下功能:

  • 读取文件
  • 写入文件
  • 遍历文件树
  • 读取和写入数据对象到文件

也可以使用java.io.*下面的所有标准Java类

读取文件

new File("/Users/hefuduo/hefuduo.profile").eachLine {
    line -> println line
}

如果要将文件的整个内容作为字符串获取,可以使用文件类的text属性。

File fi = new File("/Users/hefuduo/hefuduo.profile")
println fi.text

或者使用Java提供的InputStream

File fi = new File("/Users/hefuduo/hefuduo.profile")
InputStream it = new FileInputStream(fi)
it.eachLine {
    println it
}

写入文件

File fi = new File("/Users/hefuduo/hefuduo.profile")
fi.withWriter {
    it.append("Job : computer engineer")
}
println fi.text

以追加的形式写入文件:

File fi = new File("/Users/hefuduo/hefuduo.profile")
OutputStream stream = new FileOutputStream(fi,true)
stream.withWriter {
    it.write("age:27")
}
stream.close()
fi.eachLine {
    println it
}

获取文件大小

File fi = new File("/Users/hefuduo/hefuduo.profile")
println fi.absolutePath
println fi.length()

测试文件是否是目录

println fi.isFile()
println fi.isDirectory()

创建与删除目录

File fi = new File("/Users/hefuduo/dirtest/")
fi.mkdir()
fi.delete()

复制文件

File fi = new File("/Users/hefuduo/hefuduo.profile")
def src = fi
def dst = new File(fi.absolutePath)
dst << src.text

获取目录中的所有内容

File fi = new File("/Users/hefuduo/")
def files = fi.listFiles()
println files.length
files.each &#123;
    println it.name
&#125;

如果要递归的显示目录与子目录中的所有文件,则可以使用File了eachFileRecurse函数。

File fi = new File("/Users/hefuduo/")
fi.eachFileRecurse &#123;
    file ->
        println file.getAbsolutePath()
&#125;

Groovy 可选

Groovy是个动态类型语言,相对Java的强类型语言,Groovy在编写代码时候,可以灵活的提供类型或不是类型。

Groovy内部数据

Groovy数字

在Groovy中,数字实际上标识为对象,他们都是类Integer的一个实例。

Groovy支持整数和浮点数

Integer x = 5
Float y = 1.25

装箱与拆箱

def x = 5, y = 6, z = 0
z = x + y
println z

Groovy字符串

Groovy提供了多种表示String字面量的方法。 Groovy中的字符串可以用单引号(’),双引号(“)或三引号(”“”)括起来。此外,由三重引号括起来的Groovy字符串可以跨越多行。

字符串索引

String sample = "Hello Groovy"
println sample[4]
println sample[-1]
println sample[1..2]
println sample[4..2]
println sample[-2]

Groovy范围

范围是指定值序列的速记。范围由序列中的第一个和最后一个值表示,Range可以是包含或排除。包含范围包括从第一个到最后一个的所有值,而独占范围包括除最后一个之外的所有值。这里有一些范例文字的例子 -

  • 1..10 - 包含范围的示例
  • 1 .. <10 - 独占范围的示例
  • ‘a’..’x’ - 范围也可以由字符组成
  • 10..1 - 范围也可以按降序排列
  • ‘x’..’a’ - 范围也可以由字符组成并按降序排列。

Groovy列表

列表是用于存储数据项集合的结构。在Groovy中,List保存了一系列对象引用。List中的对象引用占据序列中的位置,并通过整数索引来区分。列表文字表示为一系列用逗号分隔并用方括号括起来的对象。

要处理列表中的数据,我们必须能够访问各个元素。 Groovy列表使用索引操作符[]索引。列表索引从零开始,这指的是第一个元素。

以下是一些列表的示例 -

  • [11,12,13,14] - 整数值列表
  • [‘Angular’,’Groovy’,’Java’] - 字符串列表
  • [1,2,[3,4],5] - 嵌套列表
  • [‘Groovy’,21,2.11] - 异构的对象引用列表
  • [] - 一个空列表

Groovy映射

映射(也称为关联数组,字典,表和散列)是对象引用的无序集合。Map集合中的元素由键值访问。 Map中使用的键可以是任何类。当我们插入到Map集合中时,需要两个值:键和值。

以下是一些映射的例子 -

  • [‘TopicName’:’Lists’,’TopicName’:’Maps’] - 具有TopicName作为键的键值对的集合及其相应的值。
  • [:] - 空映射。

在本章中,我们将讨论Groovy中可用的映射方法。

def aMap = ['TopicName':'Lists','TopicName2':'Maps']
aMap.each &#123;
    key,value ->
        println("key = $key" + "value = $value")
&#125;

Groovy正则表达式

正则表达式是用于在文本中查找子字符串的模式。 Groovy使用〜“regex”表达式本地支持正则表达式。引号中包含的文本表示用于比较的表达式。

例如,我们可以创建一个正则表达式对象,如下所示 -

def regex = ~'Groovy'

当Groovy运算符=〜在if和while语句(见第8章)中作为谓词(返回布尔值的表达式)出现时,左侧的String操作数与右侧的正则表达式操作数匹配。因此,以下每个都传递值true。

当定义正则表达式时,可以使用以下特殊字符

  • 有两个特殊的位置字符用于表示一行的开始和结束:caret(∧)和美元符号($)。
  • 正则表达式也可以包括量词。加号(+)表示一次或多次,应用于表达式的前一个元素。星号(*)用于表示零个或多个出现。问号(?)表示零或一次。
  • 元字符{和}用于匹配前一个字符的特定数量的实例。
  • 在正则表达式中,句点符号(。)可以表示任何字符。这被描述为通配符。
  • 正则表达式可以包括字符类。一组字符可以作为简单的字符序列,包含在元字符[和]中,如[aeiou]中。对于字母或数字范围,可以使用[a-z]或[a-mA-M]中的短划线分隔符。字符类的补码由方括号内的前导插入符号表示,如[∧a-z]中所示,并表示除指定的字符以外的所有字符。
'Groovy' =~ 'Groovy' 
'Groovy' =~ 'oo' 
'Groovy' ==~ 'Groovy' 
'Groovy' ==~ 'oo' 
'Groovy' =~ '∧G' 
‘Groovy' =~ 'G$' 
‘Groovy' =~ 'Gro*vy' 'Groovy' =~ 'Gro&#123;2&#125;vy'
def regex = ~'groovy'
println ("groovy".matches(regex))

面向对象

class Empire{
    private String name //必须制定getter和setter,因为是private的
    int age //提供默认的getter 和setter
    Empire(String name,int age){
        this.name = name
        this.age = age
    }
    def getName(){
        return name
    }
}

Empire empire = new Empire("China",5000)
println empire.getName() //属性getter
println empire.name  //属性
println empire.age

继承&扩展

interface FLY&#123;
    def fly()
&#125;

class Animal &#123;
    private String name
    private String category

    Animal(String name, String category) &#123;
        this.name = name
        this.category = category
    &#125;

    String getName() &#123;
        return name
    &#125;

    String getCategory() &#123;
        return category
    &#125;

    void setName(String name) &#123;
        this.name = name
    &#125;

    void setCategory(String category) &#123;
        this.category = category
    &#125;
&#125;

class Dove extends Animal implements FLY&#123;
    
    
    Dove(String name, String category) &#123;
        super(name, category)
    &#125;

    @Override
    def fly() &#123;
        println "I believe i can fly"
    &#125;
&#125;

Dove d = new Dove("Dove","Bird")
println d.name
println d.category 
d.fly()

Dove f = new Dove("","")
f.name = "lala"
f.category = "prettyBird"
println f.name
println f.category

内部类

下面是一个外部和内部类的例子。在下面的例子中,我们做了以下事情 -

  • 创建一个名为Outer的类,它将是我们的外部类。
  • 在Outer类中定义名为name的字符串。
  • 在我们的外类中创建一个内部或嵌套类。
  • 请注意,在内部类中,我们可以访问在Outer类中定义的名称实例成员。

其实Groovy中的内部类和Java中的是一样的

抽象类

同上

abstract class ABTest&#123;
    abstract def test()
&#125;

class AB extends ABTest&#123;

    @Override
    def test() &#123;
        return null
    &#125;
&#125;

接口

接口定义了类需要遵守的契约。接口仅定义需要实现的方法的列表,但是不定义方法实现。需要使用interface关键字声明接口。接口仅定义方法签名。接口的方法总是公开的。在接口中使用受保护或私有方法是一个错误。

Groovy 泛型

可以参考Java的泛型,他们是一样的。(因为Groovy也是基于JVM)

Groovy特征

特征是语言的结构构造,允许 -

  • 行为的组成。
  • 接口的运行时实现。
  • 与静态类型检查/编译的兼容性

它们可以被看作是承载默认实现和状态的接口。使用trait关键字定义trait。


trait SwimmingAbility&#123;
    def swim()&#123;
        println "$&#123;this.class&#125; is swimming"
    &#125;
&#125;

trait FlyingAbility&#123;
    def fly()&#123;
        println "$&#123;this.class&#125; is flying"
    &#125;
&#125;
interface Eat&#123;
    def eat()
&#125;

class Duck implements SwimmingAbility ,FlyingAbility,Eat&#123;

    @Override
    def eat() &#123;
        return null
    &#125;
&#125;

Duck d = new Duck()

d.fly()
d.swim()

trait Name implements FlyingAbility, SwimmingAbility&#123;
    public String name
    abstract def printName()
&#125;

class Person implements Name&#123;
    public String name

    @Override
    def printName() &#123;
        println Name__name
    &#125;
&#125;
//1.解决钻石问题(多重继承引发的歧义问题),同时能够实现多重继承的好处
//2.对trait的理解就是嵌入实现类中,而不是继承的那种上下文的关系
Person p = new Person()
p.Name__name = "hefuduo"
p.name = "LeoHe"
p.swim()
p.fly()

def foo(String str)&#123;
    str?.reverse()
&#125;

println foo(null)

##实现接口

Traits可以实现接口,在这种情况下,使用implements关键字声明接口。

属性

特征可以定义属性。下面给出了具有属性的trait的示例。

在以下示例中,integer类型的Marks1是一个属性。

class Example &#123;
   static void main(String[] args) &#123;
      Student st = new Student();
      st.StudentID = 1;
        
      println(st.DisplayMarks());
      println(st.DisplayTotal());
   &#125; 
    
   interface Total &#123;
      void DisplayTotal() 
   &#125; 
    
   trait Marks implements Total &#123;
      int Marks1;
        
      void DisplayMarks() &#123;
         this.Marks1 = 10;
         println(this.Marks1);
      &#125;
        
      void DisplayTotal() &#123;
         println("Display Total");
      &#125; 
   &#125; 
    
   class Student implements Marks &#123;
      int StudentID 
   &#125;
&#125; 

行为的构成

特征可以用于以受控的方式实现多重继承,避免钻石问题。

trait Name implements FlyingAbility, SwimmingAbility&#123;
    public String name
    abstract def printName()
&#125;

class Person implements Name&#123;
    public String name

    @Override
    def printName() &#123;
        println Name__name
    &#125;
&#125;
//1.解决钻石问题(多重继承引发的歧义问题),同时能够实现多重继承的好处
//2.对trait的理解就是嵌入实现类中,而不是继承的那种上下文的关系
Person p = new Person()
p.Name__name = "hefuduo"
p.name = "LeoHe"
p.swim()
p.fly()

Groovy闭包

闭包是一个短的匿名代码块。它通常跨越几行代码。一个方法甚至可以将代码块作为参数。它们是匿名的。

下面是一个简单闭包的例子,它是什么样子。

def close = &#123;
    println "hello groovy"
&#125;
close.call()

闭包中的形式参数

闭包也可以包含形式参数,以使它们更有用,就像Groovy中的方法一样。

def closure = &#123;
    suffix -> 
    println "hello $suffix"
&#125;

//如果不写参数suffix,默认的提供一个叫it的参数

closure.call("world")

闭包和变量

更正式地,闭包可以在定义闭包时引用变量。以下是如何实现这一点的示例。

def closure = &#123;
    param -> 
        println param
&#125;


def fin(String logan,Closure s)&#123;
    s.call(logan)
&#125;

fin("hefuduo",closure)

集合和字符串中的闭包

几个List,Map和String方法接受一个闭包作为参数。让我们看看在这些数据类型中如何使用闭包的例子。

使用闭包和列表

以下示例显示如何使用闭包与列表。在下面的例子中,我们首先定义一个简单的值列表。列表集合类型然后定义一个名为.each的函数。此函数将闭包作为参数,并将闭包应用于列表的每个元素

使用映射闭包

以下示例显示了如何使用闭包。在下面的例子中,我们首先定义一个简单的关键值项Map。然后,映射集合类型定义一个名为.each的函数。此函数将闭包作为参数,并将闭包应用于映射的每个键值对。

Groovy注解

注释是元数据的形式,其中它们提供关于不是程序本身的一部分的程序的数据。注释对它们注释的代码的操作没有直接影响。

注释主要用于以下原因 -

  • 编译器信息 -编译器可以使用注释来检测错误或抑制警告。
  • 编译时和部署时处理 -软件工具可以处理注释信息以生成代码,XML文件等。
  • 运行时处理 -一些注释可以在运行时检查。

字符串类型

@interface 
Simple&#123;
    String str1() default "hi"
&#125;

枚举类型

enum DayOfWeek&#123;Mon, Tue, Wed, Thu, Sat, Sun&#125;
@interface Scheduled&#123;
    DayOfWeek dayOfWeek()
&#125;

类类型

@interface 
Simple&#123;&#125;

@Simple
Class User&#123;
    //...
&#125;

注释成员值

使用注释时,需要至少设置所有没有默认值的成员。下面给出一个例子。当定义后使用注释示例时,需要为其分配一个值。

关闭注释参数

Groovy中注释的一个很好的特性是,你也可以使用闭包作为注释值。因此,注释可以与各种各样的表达式一起使用。

下面给出一个例子。注释Onlyif是基于类值创建的。然后注释应用于两个方法,它们基于数字变量的值向结果变量发布不同的消息。

@interface OnlyIf &#123;
   Class value() 
&#125;  

@OnlyIf(&#123; number<=6 &#125;) 
void Version6() &#123;
   result << 'Number greater than 6' 
&#125; 

@OnlyIf(&#123; number>=6 &#125;) 
void Version7() &#123;
   result << 'Number greater than 6' 
&#125;

元注释

这是groovy中注释的一个非常有用的功能。有时可能有一个方法的多个注释,如下所示。有时这可能变得麻烦有多个注释。

@Procedure 
@Master class 
MyMasterProcedure &#123;&#125; 

在这种情况下,您可以定义一个元注释,它将多个注释集中在一起,并将元注释应用于该方法。所以对于上面的例子,你可以使用AnnotationCollector来定义注释的集合。

import groovy.transform.AnnotationCollector
  
@Procedure 
@Master 
@AnnotationCollector

一旦完成,您可以应用以下元注释器到该方法 -

import groovy.transform.AnnotationCollector
  
@Procedure 
@Master 
@AnnotationCollector
  
@MasterProcedure 
class MyMasterProcedure &#123;&#125;

Groovy XML

XML 生成

import groovy.xml.MarkupBuilder

def mB = new MarkupBuilder()

// Compose the builder
mB.collection(shelf: 'New Arrivals') &#123;
    movie(title: 'Enemy Behind')
    type('War, Thriller')
    format('DVD')
    year('2003')
    rating('PG')
    stars(10)
    description('Talk about a US-Japan war')
&#125;

mB.Html()&#123;
    header(ref : "www.baidu.com")
    body&#123;
        h1("hello world")
    &#125;
&#125;

XML解析

Groovy XmlParser类使用一个简单的模型来将XML文档解析为Node实例的树。每个节点都有XML元素的名称,元素的属性和对任何子节点的引用。这个模型足够用于大多数简单的XML处理。

<collection shelf="New Arrivals">

    <movie title="Enemy Behind">
        <type>War, Thriller</type>
        <format>DVD</format>
        <year>2003</year>
        <rating>PG</rating>
        <stars>10</stars>
        <description>Talk about a US-Japan war</description>
    </movie>

    <movie title="Transformers">
        <type>Anime, Science Fiction</type>
        <format>DVD</format>
        <year>1989</year>
        <rating>R</rating>
        <stars>8</stars>
        <description>A schientific fiction</description>
    </movie>

    <movie title="Trigun">
        <type>Anime, Action</type>
        <format>DVD</format>
        <year>1986</year>
        <rating>PG</rating>
        <stars>10</stars>
        <description>Vash the Stam pede!</description>
    </movie>

    <movie title="Ishtar">
        <type>Comedy</type>
        <format>VHS</format>
        <year>1987</year>
        <rating>PG</rating>
        <stars>2</stars>
        <description>Viewable boredom</description>
    </movie>

</collection> 

解析代码

def parser = new XmlParser()
def doc = parser.parse("assets/movies.xml")
doc.movie.each &#123;
    bk ->
        print "Movie Name : "
        println "$&#123;bk['@title']&#125;"
        print "Movie Type : "
        println "$&#123;bk.type[0].text()&#125;"
        print "Movie Format : "
        println "$&#123;bk.format[0].text()&#125;"
        print "Movie year : "
        println "$&#123;bk.year[0].text()&#125;"
        print "Rating : "
        println "$&#123;bk.rating[0].text()&#125;"
        print "Starts : "
        println "$&#123;bk.stars[0].text()&#125;"
        print "Description : "
        println "$&#123;bk.description[0].text()&#125;"
        println "============================="
&#125;

重要的事情需要注意上面的代码。

  • 正在形成类XmlParser的对象,以便它可以用于解析XML文档。
  • 解析器被给定XML文件的位置。
  • 对于每个电影元素,我们使用闭包浏览每个子节点并显示相关信息。

对于movie元素本身,我们使用@符号显示附加到movie元素的title属性。

Groovy JMX

JMX 用于监控与Java虚拟机环境有任何关系的所有应用程序。

监视JVM

import java.lang.management.*

def os = ManagementFactory.operatingSystemMXBean
println """OPERATING SYSTEM: 
    OS architecture = $os.arch 
    OS name = $os.name 
    OS version = $os.version 
    OS processors = $os.availableProcessors 
"""

def rt = ManagementFactory.runtimeMXBean
println """RUNTIME: 
       Runtime name = $rt.name 
       Runtime spec name = $rt.specName 
       Runtime vendor = $rt.specVendor 
       Runtime spec version = $rt.specVersion 
       Runtime management spec version = $rt.managementSpecVersion 
   """

def mem = ManagementFactory.memoryMXBean
def heapUsage = mem.heapMemoryUsage
def nonHeapUsage = mem.nonHeapMemoryUsage

println """MEMORY: 
   HEAP STORAGE: 
          Memory committed = $heapUsage.committed 
          Memory init = $heapUsage.init 
          Memory max = $heapUsage.max 
          Memory used = $heapUsage.used NON-HEAP STORAGE: 
          Non-heap memory committed = $nonHeapUsage.committed 
          Non-heap memory init = $nonHeapUsage.init 
          Non-heap memory max = $nonHeapUsage.max 
          Non-heap memory used = $nonHeapUsage.used 
   """

println "GARBAGE COLLECTION:"
ManagementFactory.garbageCollectorMXBeans.each &#123; gc ->
    println "    name = $gc.name"
    println "        collection count = $gc.collectionCount"
    println "        collection time = $gc.collectionTime"
    String[] mpoolNames =   gc.memoryPoolNames

    mpoolNames.each &#123;
        mpoolName -> println "        mpool name = $mpoolName"
    &#125;
&#125;

Groovy JSON

JSON功能

功能
JsonSlurper JsonSlurper是一个将JSON文本或阅读器内容解析为Groovy数据的类结构,例如地图,列表和原始类型,如整数,双精度,布尔和字符串。
JsonOutput 此方法负责将Groovy对象序列化为JSON字符串。

反序列化

import groovy.json.JsonSlurper

def slurper = new JsonSlurper()
def obj = slurper.parseText('&#123;"name":"John","id":"1"&#125;')
println obj.name
println obj.id

def lst = slurper.parseText('&#123;"List" : [2,3,4,5]&#125;')
lst.each &#123;
    key, value ->
        println("$key = $value")
&#125;

序列化

import groovy.json.JsonOutput

def jsonOuter = new JsonOutput()
Map<String, Object> map = new HashMap<>()
map.put("name","hefuduo")
map.put("age",27)
map.put("ss",new Car(14))
String json = jsonOuter.toJson(map)

println json

Groovy DLS

Groovy允许在顶层语句的方法调用的参数周围省略括号。这被称为“命令链”功能。这个扩展的工作原理是允许一个人链接这种无括号的方法调用,在参数周围不需要括号,也不需要链接调用之间的点。

如果一个调用被执行为bcd,这将实际上等价于a(b).c(d)。

DSL或域特定语言旨在简化以Groovy编写的代码,使得它对于普通用户变得容易理解。以下示例显示了具有域特定语言的确切含义。

class EmailDSL&#123;
    String toText
    String fromText
    String body
    
    def static make(Closure closure)&#123;
        EmailDSL dsl = new EmailDSL()
        closure.delegate = dsl
        closure.call()
    &#125;
    
    def to(String toText)&#123;
        this.toText = toText
    &#125;
    
    def from(String fromText)&#123;
        this.fromText = fromText
    &#125;
    
    def body(String bodyText)&#123;
        this.body = bodyText
    &#125;
    def send(boolean send)&#123;
        if (send)&#123;
            println "From $fromText To $toText : $body"
        &#125;
    &#125;
&#125;

EmailDSL.make &#123;
    to "James"
    from "Leo"
    body "How is everything going on"
    send true
&#125;
  • 使用接受闭包的静态方法。这是一个很麻烦的方式来实现DSL。
  • 在电子邮件示例中,类EmailDsl具有make方法。它创建一个实例,并将闭包中的所有调用委派给实例。这是一种机制,其中“to”和“from”节结束了EmailDsl类中的执行方法。
  • 一旦to()方法被调用,我们将文本存储在实例中以便以后格式化。
  • 我们现在可以使用易于为最终用户理解的简单语言调用EmailDSL方法。

Groovy 模板引擎

字符串中的简单模板

如果你采用下面的简单例子,我们首先定义一个名称变量来保存字符串“Groovy”。在println语句中,我们使用$符号来定义可以插入值的参数或模板。

def name = "Groovy" 
println "This Tutorial is about $&#123;name&#125;"

如果上面的代码在groovy中执行,将显示以下输出。输出清楚地显示$名称被由def语句分配的值替换

简单模板引擎

以下是SimpleTemplateEngine的示例,它允许您在模板中使用类似于JSP的scriptlet和EL表达式,以生成参数化文本。模板引擎允许绑定参数列表及其值,以便可以在具有定义的占位符的字符串中替换它们。

def text ='This Tutorial focuses on $TutorialName. In this tutorial you will learn 

about $Topic'  

def binding = ["TutorialName":"Groovy", "Topic":"Templates"]  
def engine = new groovy.text.SimpleTemplateEngine() 
def template = engine.createTemplate(text).make(binding) 

println template

如果上面的代码在groovy中执行,将显示以下输出。

现在让我们使用XML文件的模板功能。作为第一步,让我们将下面的代码添加到一个名为Student.template的文件中。在以下文件中,您将注意到,我们尚未添加元素的实际值,而是添加占位符。所以$ name,$ is和$ subject都被放置为占位符,需要在运行时替换。

<Student> 
   <name>${name}</name> 
   <ID>${id}</ID> 
   <subject>${subject}</subject> 
</Student>

现在,让我们添加我们的Groovy脚本代码来添加功能,可以使用实际值替换上面的模板。应该注意以下事项关于以下代码。

  • 占位符到实际值的映射通过绑定和SimpleTemplateEngine完成。绑定是一个映射,占位符作为键,替换值作为值。
import groovy.text.* 
import java.io.* 

def file = new File("D:/Student.template") 
def binding = ['name' : 'Joe', 'id' : 1, 'subject' : 'Physics']
                  
def engine = new SimpleTemplateEngine() 
def template = engine.createTemplate(file) 
def writable = template.make(binding) 

println writable

如果上面的代码在groovy中执行,将显示以下输出。从输出中可以看出,在相关占位符中成功替换了值。

<Student> 
   <name>Joe</name> 
   <ID>1</ID> 
   <subject>Physics</subject> 
</Student>

Groovy 元对象编程

元对象编程或MOP可以用于动态调用方法,并且可以即时创建类和方法。

那么这是什么意思呢?让我们考虑一个叫Student的类,它是一个没有成员变量或方法的空类。假设你必须在这个类上调用以下语句。

Def myStudent = new Student() 
myStudent.Name = ”Joe”; 
myStudent.Display()

现在在元对象编程中,即使类没有成员变量Name或方法Display(),上面的代码仍然可以工作。

这如何工作?那么,为了这个工作,一个人必须实现GroovyInterceptable接口挂钩到Groovy的执行过程。以下是该接口的可用方法。

Public interface GroovyInterceptable &#123; 
   Public object invokeMethod(String methodName, Object args) 
   Public object getproperty(String propertyName) 
   Public object setProperty(String propertyName, Object newValue) 
   Public MetaClass getMetaClass() 
   Public void setMetaClass(MetaClass metaClass) 
&#125;

所以在上面的接口描述中,假设你必须实现invokeMethod(),它会被调用的每个方法,要么存在或不存在。

缺失属性

所以,让我们看一个例子,我们如何为缺失的属性实现元对象编程。以下键应该注意以下代码。

  • 类Student没有定义名为Name或ID的成员变量。
  • 类Student实现GroovyInterceptable接口。
  • 有一个称为dynamicProps的参数,将用于保存即时创建的成员变量的值。
  • 方法getproperty和setproperty已被实现以在运行时获取和设置类的属性的值。
class Example &#123;
   static void main(String[] args) &#123;
      Student mst = new Student();
      mst.Name = "Joe";
      mst.ID = 1;
        
      println(mst.Name);
      println(mst.ID);
   &#125;
&#125;

class Student implements GroovyInterceptable &#123; 
   protected dynamicProps=[:]
    
   void setProperty(String pName,val) &#123;
      dynamicProps[pName] = val
   &#125;
   
   def getProperty(String pName) &#123;
      dynamicProps[pName]
   &#125; 
&#125; 

以下代码的输出将是 -

Joe 
1

缺失方法

所以,让我们看一个例子,我们如何为缺失的属性实现元对象编程。以下键应该注意下面的代码 -

  • 类学生现在实现invokeMethod方法,它被调用,而不管该方法是否存在。
class Example {
   static void main(String[] args) {
      Student mst = new Student();
      mst.Name = "Joe";
      mst.ID = 1;
        
      println(mst.Name);
      println(mst.ID);
      mst.AddMarks();
   } 
}
 
class Student implements GroovyInterceptable {
   protected dynamicProps = [:]  
    
   void setProperty(String pName, val) {
      dynamicProps[pName] = val
   } 
   
   def getProperty(String pName) {
      dynamicProps[pName]
   }
   
   def invokeMethod(String name, Object args) {
      return "called invokeMethod $name $args"
   }
}

以下代码的输出如下所示。请注意,即使方法Display不存在,也没有缺少方法异常的错误。

Joe 
1 

Comments

2017-08-03

⬆︎TOP