news 2026/2/28 16:03:42

Groovy 脚本语法全解析:从入门到精通的干货指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Groovy 脚本语法全解析:从入门到精通的干货指南

Groovy作为一门基于JVM的动态编程语言,兼具Java的稳定性与脚本语言的灵活性,在自动化测试、DevOps、数据处理等领域被广泛应用。本文将从基础语法到高级特性,结合大量可直接编译运行的实例,深入剖析Groovy脚本的核心逻辑与实战技巧,帮助开发者快速掌握这门高效的编程语言。

一、Groovy简介与环境搭建
1.1 什么是Groovy?

Groovy是一门基于JVM的动态脚本语言,由James Strachan于2003年创建。它完全兼容Java语法,同时吸收了Python、Ruby等脚本语言的优点,提供了更简洁的语法、强大的元编程能力和丰富的内置API。Groovy代码可以直接编译为Java字节码,无缝运行在JVM上,并且能与Java类库完美互操作。

权威来源:Groovy官方文档

1.2 环境搭建(Maven方式)

在Java项目中集成Groovy,推荐使用Maven管理依赖。以下是完整的pom.xml配置(基于最新稳定版Groovy 4.0.20):

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jam.demo</groupId> <artifactId>groovy-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <groovy.version>4.0.20</groovy.version> <lombok.version>1.18.30</lombok.version> <spring-boot.version>3.2.5</spring-boot.version> <fastjson2.version>2.0.47</fastjson2.version> <mybatis-plus.version>3.5.5</mybatis-plus.version> <mysql.version>8.0.36</mysql.version> </properties> <!-- 父依赖:Spring Boot 最新稳定版 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring-boot.version}</version> <relativePath/> </parent> <dependencies> <!-- Groovy核心依赖 --> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>${groovy.version}</version> <type>pom</type> </dependency> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <!-- FastJSON2 --> <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>${fastjson2.version}</version> </dependency> <!-- MyBatis-Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>${mysql.version}</version> <scope>runtime</scope> </dependency> <!-- Swagger3 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency> </dependencies> <build> <plugins> <!-- Groovy编译插件 --> <plugin> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>3.0.0</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding> <targetBytecodeVersion>17</targetBytecodeVersion> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
二、Groovy基础语法(兼容Java,更简洁)
2.1 变量定义

Groovy支持动态类型和静态类型,变量定义可省略类型声明(使用def关键字),也可显式指定类型(兼容Java)。

2.1.1 基本语法
/** * 变量定义示例 * @author ken */ // 动态类型:def关键字,类型由赋值自动推断 def name = "Groovy" // String类型 def age = 30 // Integer类型 def isEnable = true // Boolean类型 // 静态类型:显式指定类型(兼容Java) String title = "Groovy语法指南" Integer count = 100 Boolean flag = false // 常量定义:final关键字(与Java一致) final String VERSION = "4.0.20"
2.1.2 变量作用域

Groovy的变量作用域与Java类似,分为局部变量、成员变量、静态变量:

/** * 变量作用域示例 * @author ken */ class VariableScopeDemo { // 成员变量(实例变量) String memberVar = "成员变量" // 静态变量(类变量) static String staticVar = "静态变量" void testScope() { // 局部变量 def localVar = "局部变量" println("局部变量:${localVar}") println("成员变量:${memberVar}") println("静态变量:${staticVar}") } } // 运行测试 new VariableScopeDemo().testScope() // 输出结果: // 局部变量:局部变量 // 成员变量:成员变量 // 静态变量:静态变量
2.2 数据类型

Groovy的核心数据类型与Java一致(基本类型+包装类型),同时扩展了部分实用类型(如GString、Range等)。

2.2.1 基本数据类型(与Java完全兼容)
/** * 基本数据类型示例 * @author ken */ // 整数类型 byte b = 10 short s = 200 int i = 1000 long l = 10000000000L // 浮点类型 float f = 3.14f double d = 3.1415926 // 字符类型 char c = 'A' // 布尔类型 boolean bool = true // 输出类型信息 println("byte类型:${b.class}") // class java.lang.Byte println("int类型:${i.class}") // class java.lang.Integer println("double类型:${d.class}") // class java.lang.Double println("char类型:${c.class}") // class java.lang.Character
2.2.2 Groovy扩展类型
  • GString(可插值字符串):支持在字符串中直接插入变量或表达式,用${}包裹,比Java的字符串拼接更简洁。

/** * GString示例 * @author ken */ def name = "Groovy" def version = "4.0.20" // 单引号字符串:无插值功能(与Java一致) def singleQuoteStr = 'Hello, ${name}!' // 双引号字符串:支持插值(GString类型) def doubleQuoteStr = "Hello, ${name}! Version: ${version}" // 三引号字符串:支持多行文本和插值 def multiLineStr = """ Hello, ${name}! This is a multi-line string. Version: ${version} """ println(singleQuoteStr) // 输出:Hello, ${name}! println(doubleQuoteStr) // 输出:Hello, Groovy! Version: 4.0.20 println(multiLineStr) // 输出: // Hello, Groovy! // This is a multi-line string. // Version: 4.0.20 // 验证类型 println(doubleQuoteStr.class) // class groovy.lang.GString println(singleQuoteStr.class) // class java.lang.String
  • Range(范围类型):表示一个连续的区间,支持整数、字符、日期等类型,语法为start..end(闭区间)或start..<end(左闭右开区间)。

/** * Range示例 * @author ken */ // 整数范围(闭区间:1到5,包含5) def intRange = 1..5 // 整数范围(左闭右开:1到5,不包含5) def intRangeOpen = 1..<5 // 字符范围 def charRange = 'a'..'e' // 日期范围 def dateStart = new Date() def dateEnd = new Date() + 7 // 7天后 def dateRange = dateStart..dateEnd // 遍历Range println("整数闭区间遍历:") intRange.each { println(it) } // 输出1,2,3,4,5 println("整数左闭右开区间遍历:") intRangeOpen.each { println(it) } // 输出1,2,3,4 println("字符范围遍历:") charRange.each { println(it) } // 输出a,b,c,d,e // Range常用方法 println("intRange是否包含3:${intRange.contains(3)}") // true println("intRange的长度:${intRange.size()}") // 5 println("intRange的第一个元素:${intRange.from}") // 1 println("intRange的最后一个元素:${intRange.to}") // 5
2.3 运算符

Groovy支持Java的所有运算符,同时扩展了部分实用运算符,让代码更简洁。

2.3.1 基础运算符(与Java一致)
/** * 基础运算符示例 * @author ken */ def a = 10 def b = 3 // 算术运算符 println("a + b = ${a + b}") // 13 println("a - b = ${a - b}") // 7 println("a * b = ${a * b}") // 30 println("a / b = ${a / b}") // 3.3333333333333335 println("a % b = ${a % b}") // 1 println("a ** b = ${a ** b}") // 1000(幂运算,Groovy扩展) // 比较运算符 println("a == b ? ${a == b}") // false println("a > b ? ${a > b}") // true println("a < b ? ${a < b}") // false // 逻辑运算符 def flag1 = true def flag2 = false println("flag1 && flag2 = ${flag1 && flag2}") // false println("flag1 || flag2 = ${flag1 || flag2}") // true println("!flag1 = ${!flag1}") // false
2.3.2 Groovy扩展运算符
  • 安全导航运算符(?.):避免空指针异常,当对象为null时,表达式直接返回null,而非抛出异常。

/** * 安全导航运算符示例 * @author ken */ class User { String name Integer age } def user1 = new User(name: "张三", age: 25) def user2 = null // 正常访问(无空指针) println("user1姓名:${user1?.name}") // 张三 // 当对象为null时,返回null(不抛出异常) println("user2姓名:${user2?.name}") // null // 对比Java:若直接user2.name会抛出NullPointerException
  • ** Elvis运算符(?:)**:简化空值判断,等价于obj != null ? obj : 默认值

/** * Elvis运算符示例 * @author ken */ def user = null def defaultName = "未知用户" // 等价于:def userName = user != null ? user.name : defaultName def userName = user?.name ?: defaultName println(userName) // 未知用户 // 非空场景 def user3 = new User(name: "李四", age: 30) def userName3 = user3?.name ?: defaultName println(userName3) // 李四
  • 安全类型转换运算符(as?):安全地进行类型转换,转换失败时返回null,而非抛出ClassCastException。

/** * 安全类型转换运算符示例 * @author ken */ def obj1 = "123" def obj2 = "abc" // 安全转换为Integer:转换成功返回对应类型 def num1 = obj1 as? Integer println("num1类型:${num1.class},值:${num1}") // class java.lang.Integer,值:123 // 转换失败返回null def num2 = obj2 as? Integer println("num2:${num2}") // null // 对比Java:(Integer)obj2会抛出ClassCastException
  • 展开运算符(*):用于集合或数组的展开,将集合元素作为独立参数传递给方法。

/** * 展开运算符示例 * @author ken */ def list = [1, 2, 3] // 定义一个接收多个参数的方法 def sum(a, b, c) { return a + b + c } // 使用展开运算符传递集合元素 def total = sum(*list) println("总和:${total}") // 6 // 字符串展开(转换为字符数组) def str = "abc" def charList = [*str] println(charList) // [a, b, c]
2.4 流程控制语句

Groovy的流程控制语句与Java基本一致,包括if-elseforwhileswitch等,但语法更简洁。

2.4.1 if-else(与Java一致)
/** * if-else示例 * @author ken */ def score = 85 if (score >= 90) { println("优秀") } else if (score >= 80) { println("良好") } else if (score >= 60) { println("及格") } else { println("不及格") } // 输出:良好
2.4.2 for循环(扩展了更简洁的语法)
/** * for循环示例 * @author ken */ // 1. 传统for循环(与Java一致) for (int i = 0; i < 5; i++) { println("传统循环:${i}") } // 2. 增强for循环(遍历集合/数组,与Java一致) def list = [1, 2, 3, 4, 5] for (def item : list) { println("增强循环:${item}") } // 3. Groovy简化循环(遍历Range) for (i in 1..5) { println("简化循环:${i}") } // 4. 遍历Map def map = [name: "Groovy", version: "4.0.20", author: "James Strachan"] for (entry in map) { println("Map键:${entry.key},值:${entry.value}") }
2.4.3 while与do-while(与Java一致)
/** * while与do-while示例 * @author ken */ // while循环 def count = 0 while (count < 3) { println("while循环:${count}") count++ } // do-while循环(至少执行一次) def num = 0 do { println("do-while循环:${num}") num++ } while (num < 3)
2.4.4 switch-case(支持更多类型,更灵活)

Groovy的switch支持整数、字符串、表达式、类型判断等,比Java更强大。

/** * switch-case示例 * @author ken */ def obj = "Groovy" switch (obj) { case 1: println("整数1") break case "Groovy": println("字符串:Groovy") break case [1, 2, 3]: println("集合中的元素") break case Integer: println("Integer类型") break case String: println("String类型") break default: println("默认情况") } // 输出:字符串:Groovy
三、Groovy核心特性(相比Java的优势)
3.1 集合操作(List、Map、Set)

Groovy对Java集合进行了深度扩展,提供了大量便捷的方法,让集合操作更简洁高效。

3.1.1 List(列表)

Groovy的List默认是java.util.ArrayList,定义语法为[元素1, 元素2, ...]

/** * List操作示例 * @author ken */ // 定义List(默认ArrayList) def list = [1, 2, 3, 4, 5] // 1. 访问元素 println("第一个元素:${list[0]}") // 1 println("最后一个元素:${list[-1]}") // 5(Groovy扩展:负索引表示从尾部开始) println("索引1到3的元素:${list[1..3]}") // [2, 3, 4](Range切片) // 2. 添加元素 list.add(6) list << 7 // Groovy扩展:左移运算符等价于add() list.addAll([8, 9]) println("添加元素后:${list}") // [1, 2, 3, 4, 5, 6, 7, 8, 9] // 3. 删除元素 list.remove(0) // 删除索引0的元素 list.remove(Integer.valueOf(9)) // 删除值为9的元素 list -= 8 // Groovy扩展:减号运算符等价于remove() println("删除元素后:${list}") // [2, 3, 4, 5, 6, 7] // 4. 遍历List // 方式1:each方法(最常用) println("each遍历:") list.each { element -> println(element) } // 方式2:eachWithIndex(带索引遍历) println("eachWithIndex遍历:") list.eachWithIndex { element, index -> println("索引${index}:${element}") } // 5. 过滤与转换(高阶函数) // filter:筛选出偶数 def evenList = list.findAll { it % 2 == 0 } println("偶数列表:${evenList}") // [2, 4, 6] // map:将每个元素乘以2 def doubleList = list.collect { it * 2 } println("元素翻倍后:${doubleList}") // [4, 6, 8, 10, 12, 14] // 6. 聚合操作 def sum = list.sum() // 求和 def max = list.max() // 最大值 def min = list.min() // 最小值 def average = list.average() // 平均值 println("求和:${sum},最大值:${max},最小值:${min},平均值:${average}") // 输出:求和:27,最大值:7,最小值:2,平均值:4.5
3.1.2 Map(映射)

Groovy的Map默认是java.util.LinkedHashMap,定义语法为[键1: 值1, 键2: 值2, ...],键默认是字符串(可省略引号)。

/** * Map操作示例 * @author ken */ // 定义Map(键默认是字符串,可省略引号) def map = [name: "Groovy", version: "4.0.20", author: "James Strachan"] // 1. 访问元素 println("name:${map.name}") // Groovy(点语法,推荐) println("version:${map['version']}") // 4.0.20(括号语法) println("author:${map.get('author')}") // James Strachan(get方法) println("不存在的键:${map.get('desc', '无描述')}") // 无描述(get方法带默认值) // 2. 添加元素 map.desc = "基于JVM的动态脚本语言" // 点语法添加 map.put("year", 2003) // put方法添加 map << [language: "Java"] // 左移运算符添加 println("添加元素后:${map}") // 输出:[name:Groovy, version:4.0.20, author:James Strachan, desc:基于JVM的动态脚本语言, year:2003, language:Java] // 3. 删除元素 map.remove("year") map -= "language" // 减号运算符删除 println("删除元素后:${map}") // 输出:[name:Groovy, version:4.0.20, author:James Strachan, desc:基于JVM的动态脚本语言] // 4. 遍历Map // 方式1:遍历entry map.each { entry -> println("键:${entry.key},值:${entry.value}") } // 方式2:遍历键和值(解构) map.each { key, value -> println("键:${key},值:${value}") } // 方式3:遍历键或值 println("遍历键:") map.keySet().each { println(it) } println("遍历值:") map.values().each { println(it) } // 5. 过滤与转换 // 筛选出值长度大于5的entry def filteredMap = map.findAll { key, value -> value.toString().length() > 5 } println("筛选后:${filteredMap}") // 输出:[name:Groovy, author:James Strachan, desc:基于JVM的动态脚本语言] // 转换为List(键值对数组) def entryList = map.collect { key, value -> [key: key, value: value] } println("转换为List:${entryList}")
3.1.3 Set(集合)

Groovy的Set默认是java.util.LinkedHashSet,定义语法为[元素1, 元素2, ...] as Setnew HashSet([元素1, 元素2, ...])

/** * Set操作示例 * @author ken */ // 定义Set(去重特性) def set = [1, 2, 2, 3, 3, 3] as Set println("Set初始化(去重):${set}") // [1, 2, 3] // 1. 添加元素 set.add(4) set << 5 println("添加元素后:${set}") // [1, 2, 3, 4, 5] // 2. 删除元素 set.remove(2) set -= 3 println("删除元素后:${set}") // [1, 4, 5] // 3. 集合运算 def set1 = [1, 2, 3, 4] as Set def set2 = [3, 4, 5, 6] as Set // 并集 def union = set1 + set2 println("并集:${union}") // [1, 2, 3, 4, 5, 6] // 交集 def intersection = set1.intersect(set2) println("交集:${intersection}") // [3, 4] // 差集(set1 - set2) def difference = set1 - set2 println("差集:${difference}") // [1, 2] // 4. 遍历Set set1.each { println(it) }
3.2 方法定义与调用

Groovy的方法定义比Java更简洁,支持可选参数、默认参数、变长参数,且方法调用时可省略括号(特殊场景)。

3.2.1 基础方法定义
/** * 基础方法定义示例 * @author ken */ // 无返回值方法(返回类型可省略,默认void) def printHello(String name) { println("Hello, ${name}!") } // 有返回值方法(返回类型可省略,由return自动推断) def add(Integer a, Integer b) { a + b // Groovy可省略return,最后一行表达式结果即为返回值 } // 显式指定返回类型(兼容Java) Integer multiply(Integer a, Integer b) { return a * b } // 方法调用 printHello("Groovy") // 输出:Hello, Groovy! def sum = add(10, 20) println("10 + 20 = ${sum}") // 30 def product = multiply(10, 20) println("10 * 20 = ${product}") // 200
3.2.2 可选参数与默认参数

Groovy支持为方法参数设置默认值,调用时可省略默认参数。

/** * 可选参数与默认参数示例 * @author ken */ // 带默认参数的方法 def sayHello(String name, String prefix = "Hello", String suffix = "!") { "${prefix}, ${name}${suffix}" } // 调用方法:传入所有参数 def msg1 = sayHello("张三", "Hi", "!!!") println(msg1) // Hi, 张三!!! // 调用方法:省略部分默认参数(按顺序省略) def msg2 = sayHello("李四") println(msg2) // Hello, 李四! // 调用方法:指定参数名传递(可打乱顺序) def msg3 = sayHello(suffix: "~", name: "王五", prefix: "Hi") println(msg3) // Hi, 王五~
3.2.3 变长参数

与Java类似,使用...表示变长参数,变长参数必须位于方法参数列表的最后。

/** * 变长参数示例 * @author ken */ // 变长参数方法(int... args 等价于 def... args) def sumAll(Integer... args) { args.sum() // 变长参数可直接作为数组处理 } // 调用方法:传入多个参数 def total1 = sumAll(1, 2, 3, 4, 5) println("总和1:${total1}") // 15 // 调用方法:传入数组(需使用展开运算符) def nums = [6, 7, 8] def total2 = sumAll(*nums) println("总和2:${total2}") // 21
3.2.4 方法调用省略括号

当方法调用是语句的最后一个表达式,且参数明确时,可省略括号(增强可读性)。

/** * 方法调用省略括号示例 * @author ken */ def printMsg(String msg) { println(msg) } // 省略括号调用(参数明确) printMsg "Hello, Groovy!" // 等价于 printMsg("Hello, Groovy!") // 链式调用省略括号 def appendStr(String str1, String str2) { str1 + str2 } def result = appendStr "Hello, ", "Groovy" println result // 等价于 println(result)
3.3 类与对象

Groovy的类定义兼容Java,同时提供了大量语法糖,如简化的构造器、属性访问器、静态导入等。

3.3.1 简化的类定义

Groovy自动为类的成员变量生成getter和setter方法,无需手动编写。

/** * 简化类定义示例 * @author ken */ // 定义类(成员变量自动生成getter/setter) class User { // 成员变量(默认是private,通过getter/setter访问) String name Integer age String email } // 创建对象(使用Map构造器,无需手动编写构造器) def user = new User(name: "张三", age: 25, email: "zhangsan@example.com") // 访问属性(通过自动生成的getter/setter,语法更简洁) println("姓名:${user.name}") // 等价于 user.getName() println("年龄:${user.age}") // 等价于 user.getAge() // 修改属性(通过自动生成的setter) user.age = 26 println("修改后的年龄:${user.age}") // 26
3.3.2 构造器

Groovy默认提供无参构造器和基于成员变量的Map构造器,也可手动定义构造器。

/** * 构造器示例 * @author ken */ class User { String name Integer age // 手动定义构造器(覆盖默认构造器) User(String name, Integer age) { this.name = name this.age = age } // 重写toString方法(Groovy可通过@ToString注解简化,后续介绍) @Override String toString() { "User{name='${name}', age=${age}}" } } // 使用手动定义的构造器创建对象 def user1 = new User("李四", 30) println(user1) // User{name='李四', age=30} // 若要使用Map构造器,需显式定义无参构造器 class User2 { String name Integer age // 无参构造器 User2() {} // 重写toString @Override String toString() { "User2{name='${name}', age=${age}}" } } // 使用Map构造器创建对象 def user2 = new User2(name: "王五", age: 35) println(user2) // User2{name='王五', age=35}
3.3.3 Groovy注解简化类定义

Groovy提供了一系列注解,可大幅简化类的编写,如@ToString@EqualsAndHashCode@TupleConstructor@Data等(类似Lombok)。

/** * Groovy注解简化类定义示例 * @author ken */ import groovy.transform.ToString import groovy.transform.EqualsAndHashCode import groovy.transform.TupleConstructor import groovy.transform.Data // @ToString:自动生成toString方法 @ToString class User3 { String name Integer age } def user3 = new User3("赵六", 40) println(user3) // User3(赵六, 40) // @EqualsAndHashCode:自动生成equals和hashCode方法 @EqualsAndHashCode class User4 { String name Integer age } def user4_1 = new User4("孙七", 45) def user4_2 = new User4("孙七", 45) def user4_3 = new User4("周八", 50) println("user4_1 == user4_2 ? ${user4_1 == user4_2}") // true println("user4_1 == user4_3 ? ${user4_1 == user4_3}") // false // @TupleConstructor:自动生成带所有成员变量的构造器 @TupleConstructor class User5 { String name Integer age } def user5 = new User5("吴九", 55) println(user5.name) // 吴九 println(user5.age) // 55 // @Data:组合@ToString、@EqualsAndHashCode、@TupleConstructor、@Getter、@Setter @Data class User6 { String name Integer age } def user6 = new User6("郑十", 60) println(user6) // User6(郑十, 60) def user6_2 = new User6("郑十", 60) println("user6 == user6_2 ? ${user6 == user6_2}") // true
3.4 闭包(Closure)

闭包是Groovy的核心特性之一,它是一段可执行的代码块,能够捕获上下文的变量和方法。闭包可作为参数传递、赋值给变量,极大增强了代码的灵活性。

3.4.1 闭包基础定义与调用

闭包的定义语法为{ [参数列表] -> 代码块 },参数列表可选,若省略参数列表,默认有一个it参数(代表闭包的唯一参数)。

/** * 闭包基础示例 * @author ken */ // 1. 无参数闭包 def noParamClosure = { println("无参数闭包") } // 调用闭包(两种方式) noParamClosure() noParamClosure.call() // 2. 带参数闭包(显式定义参数) def paramClosure = { String name, Integer age -> println("姓名:${name},年龄:${age}") } paramClosure("张三", 25) paramClosure.call("李四", 30) // 3. 单参数闭包(可省略参数列表,使用默认参数it) def singleParamClosure = { println("默认参数it:${it}") } singleParamClosure("Groovy") // 输出:默认参数it:Groovy
3.4.2 闭包作为方法参数

闭包最常用的场景是作为方法参数,实现回调功能(类似Java的Lambda表达式,但更灵活)。

/** * 闭包作为方法参数示例 * @author ken */ // 定义接收闭包参数的方法 def processList(List list, Closure closure) { list.each { element -> closure.call(element) // 调用闭包 } } // 调用方法,传递闭包 def list = [1, 2, 3, 4, 5] processList(list) { element -> println("元素:${element * 2}") } // 输出: // 元素:2 // 元素:4 // 元素:6 // 元素:8 // 元素:10 // 简化写法(闭包作为最后一个参数时,可放在方法括号外) processList(list) { println("元素:${it * 3}") }
3.4.3 闭包捕获上下文变量

闭包能够捕获定义它的上下文环境中的变量和方法,即使在上下文之外执行也能访问。

/** * 闭包捕获上下文变量示例 * @author ken */ def outerVar = "外部变量" // 定义闭包,捕获外部变量 def closure = { println("捕获外部变量:${outerVar}") } // 在上下文之外调用闭包 closure() // 输出:捕获外部变量:外部变量 // 修改外部变量,闭包中也会同步变化 outerVar = "修改后的外部变量" closure() // 输出:捕获外部变量:修改后的外部变量 // 闭包中修改外部变量 def count = 0 def incrementClosure = { count++ } incrementClosure() incrementClosure() println("count:${count}") // 输出:2
3.4.4 闭包的委托(Delegate)

Groovy闭包有三个重要的属性:thisownerdelegate,用于指定闭包执行时的上下文。

  • this:表示定义闭包的类的实例。

  • owner:表示定义闭包的上下文(可能是类或另一个闭包)。

  • delegate:默认与owner一致,可手动修改,用于实现委托模式。

/** * 闭包委托示例 * @author ken */ class DelegateDemo { String name = "DelegateDemo" void testClosure() { def closure = { println("this:${this.name}") println("owner:${owner.name}") println("delegate:${delegate.name}") } closure() } } new DelegateDemo().testClosure() // 输出: // this:DelegateDemo // owner:DelegateDemo // delegate:DelegateDemo // 修改delegate class AnotherDelegate { String name = "AnotherDelegate" } def demo = new DelegateDemo() def anotherDelegate = new AnotherDelegate() def closure = { println("delegate:${delegate.name}") } closure.delegate = anotherDelegate closure() // 输出:delegate:AnotherDelegate
四、Groovy高级特性
4.1 元编程(Metaprogramming)

Groovy基于JVM实现了强大的元编程能力,允许在运行时动态修改类的行为、添加方法、拦截方法调用等。核心机制包括:Expando、方法拦截、元类(MetaClass)。

4.1.1 Expando(动态类)

Expando允许动态创建类和添加属性、方法,适合快速构建临时对象。

/** * Expando示例 * @author ken */ // 创建Expando对象 def user = new Expando() // 动态添加属性 user.name = "张三" user.age = 25 // 动态添加方法 user.sayHello = { println("Hello, ${name}!") } user.calculate = { Integer a, Integer b -> a + b } // 调用属性和方法 println("姓名:${user.name},年龄:${user.age}") // 姓名:张三,年龄:25 user.sayHello() // 输出:Hello, 张三! def sum = user.calculate(10, 20) println("10 + 20 = ${sum}") // 30
4.1.2 元类(MetaClass)

Groovy为每个类都提供了一个元类(MetaClass),通过元类可以动态添加静态方法、实例方法,或修改现有方法的行为。

/** * MetaClass示例 * @author ken */ class User { String name Integer age } // 1. 动态为User类添加实例方法 User.metaClass.sayHello = { println("Hello, ${name}! 年龄:${age}") } // 2. 动态为User类添加静态方法 User.metaClass.static.createUser = { String name, Integer age -> new User(name: name, age: age) } // 3. 调用动态添加的方法 def user = User.createUser("李四", 30) user.sayHello() // 输出:Hello, 李四! 年龄:30 // 4. 拦截方法调用(重写invokeMethod) User.metaClass.invokeMethod = { String methodName, Object[] args -> if (methodName == "sayHello") { println("拦截sayHello方法,自定义实现:Hello, ${delegate.name}(拦截后)") } else { // 调用原方法 delegate.metaClass.getMetaMethod(methodName, args).invoke(delegate, args) } } user.sayHello() // 输出:拦截sayHello方法,自定义实现:Hello, 李四(拦截后)
4.2 异常处理

Groovy的异常处理与Java基本一致,支持try-catch-finally、抛出异常,但语法更简洁,且支持多异常捕获。

/** * 异常处理示例 * @author ken */ def divide(Integer a, Integer b) { if (b == 0) { // 抛出异常(与Java一致) throw new ArithmeticException("除数不能为0") } a / b } try { def result = divide(10, 0) println("结果:${result}") } catch (ArithmeticException e) { // 捕获特定异常 println("捕获异常:${e.message}") } catch (Exception e) { // 捕获通用异常 println("捕获通用异常:${e.message}") } finally { // 最终执行块(无论是否异常都会执行) println("异常处理结束") } // 多异常捕获(Groovy 3.0+支持,与Java 7+一致) try { def list = [1, 2, 3] println(list[10]) // 数组越界异常 divide(10, 0) // 算术异常 } catch (ArrayIndexOutOfBoundsException | ArithmeticException e) { println("捕获多类型异常:${e.message}") } finally { println("多异常处理结束") }
4.3 文件操作

Groovy提供了简洁的文件操作API,相比Java的IO操作更高效,无需手动关闭流(自动资源管理)。

4.3.1 读取文件
/** * 文件读取示例 * @author ken */ // 1. 读取文件所有内容(一行代码) def content = new File("test.txt").text println("文件内容:${content}") // 2. 按行读取文件 def file = new File("test.txt") // 方式1:eachLine(自动关闭流) file.eachLine { line, lineNum -> println("第${lineNum}行:${line}") } // 方式2:readLines(返回所有行的List) def lines = file.readLines() lines.each { println(it) } // 3. 读取文件流(手动处理) file.withReader { reader -> String line while ((line = reader.readLine()) != null) { println(line) } }
4.3.2 写入文件
/** * 文件写入示例 * @author ken */ def file = new File("output.txt") // 1. 覆盖写入文件(一行代码) file.text = "Hello, Groovy!\n这是覆盖写入的内容" // 2. 追加写入文件 file.append("\n这是追加的内容") // 3. 按行写入文件 file.withWriter { writer -> writer.writeLine("第一行内容") writer.writeLine("第二行内容") } // 4. 写入集合内容 def lines = ["第三行内容", "第四行内容"] file.withWriter { writer -> lines.each { writer.writeLine(it) } }
4.4 数据库操作(结合MyBatis-Plus)

Groovy可无缝集成MyBatis-Plus,实现数据库操作。以下是完整的实战示例(基于MySQL 8.0)。

4.4.1 配置文件(application.yml)
spring: datasource: url: jdbc:mysql://localhost:3306/groovy_demo?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.jam.demo.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl swagger: enabled: true
4.4.2 实体类(User.groovy)
/** * 用户实体类 * @author ken */ package com.jam.demo.entity import com.baomidou.mybatisplus.annotation.IdType import com.baomidou.mybatisplus.annotation.TableId import com.baomidou.mybatisplus.annotation.TableName import io.swagger.annotations.ApiModel import io.swagger.annotations.ApiModelProperty import lombok.Data import java.time.LocalDateTime @Data @TableName("t_user") @ApiModel(description = "用户实体") class User { @TableId(type = IdType.AUTO) @ApiModelProperty(value = "主键ID") Integer id @ApiModelProperty(value = "用户名") String username @ApiModelProperty(value = "密码") String password @ApiModelProperty(value = "年龄") Integer age @ApiModelProperty(value = "创建时间") LocalDateTime createTime @ApiModelProperty(value = "更新时间") LocalDateTime updateTime }
4.4.3 Mapper接口(UserMapper.groovy)
/** * 用户Mapper * @author ken */ package com.jam.demo.mapper import com.baomidou.mybatisplus.core.mapper.BaseMapper import com.jam.demo.entity.User import org.springframework.stereotype.Repository @Repository interface UserMapper extends BaseMapper<User> { }
4.4.4 服务类(UserService.groovy)
/** * 用户服务类 * @author ken */ package com.jam.demo.service import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl import com.jam.demo.entity.User import com.jam.demo.mapper.UserMapper import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @Service class UserService extends ServiceImpl<UserMapper, User> { /** * 新增用户 * @param user 用户实体 * @return 新增成功的用户 */ @Transactional(rollbackFor = Exception.class) User addUser(User user) { save(user) return user } /** * 根据ID查询用户 * @param id 主键ID * @return 用户实体 */ User getUserById(Integer id) { getById(id) } /** * 更新用户 * @param user 用户实体 * @return 是否更新成功 */ @Transactional(rollbackFor = Exception.class) boolean updateUser(User user) { updateById(user) } /** * 根据ID删除用户 * @param id 主键ID * @return 是否删除成功 */ @Transactional(rollbackFor = Exception.class) boolean deleteUser(Integer id) { removeById(id) } }
4.4.5 控制器(UserController.groovy)
/** * 用户控制器 * @author ken */ package com.jam.demo.controller import com.jam.demo.entity.User import com.jam.demo.service.UserService import io.swagger.annotations.Api import io.swagger.annotations.ApiOperation import io.swagger.annotations.ApiParam import lombok.extern.slf4j.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.util.ObjectUtils import org.springframework.web.bind.annotation.* @RestController @RequestMapping("/user") @Api(tags = "用户管理接口") @Slf4j class UserController { @Autowired UserService userService @PostMapping("/add") @ApiOperation("新增用户") User addUser(@ApiParam(value = "用户实体", required = true) @RequestBody User user) { log.info("新增用户:{}", user) return userService.addUser(user) } @GetMapping("/{id}") @ApiOperation("根据ID查询用户") User getUserById(@ApiParam(value = "用户ID", required = true) @PathVariable Integer id) { log.info("查询用户ID:{}", id) User user = userService.getUserById(id) if (ObjectUtils.isEmpty(user)) { log.warn("用户ID:{} 不存在", id) } return user } @PutMapping("/update") @ApiOperation("更新用户") boolean updateUser(@ApiParam(value = "用户实体", required = true) @RequestBody User user) { log.info("更新用户:{}", user) if (ObjectUtils.isEmpty(user.getId())) { throw new IllegalArgumentException("用户ID不能为空") } return userService.updateUser(user) } @DeleteMapping("/{id}") @ApiOperation("根据ID删除用户") boolean deleteUser(@ApiParam(value = "用户ID", required = true) @PathVariable Integer id) { log.info("删除用户ID:{}", id) return userService.deleteUser(id) } }
4.4.6 启动类(GroovyDemoApplication.groovy)
/** * 启动类 * @author ken */ package com.jam.demo import org.mybatis.spring.annotation.MapperScan import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication @SpringBootApplication @MapperScan("com.jam.demo.mapper") class GroovyDemoApplication { static void main(String[] args) { SpringApplication.run(GroovyDemoApplication, args) } }
4.4.7 SQL脚本(创建表t_user)
CREATE TABLE `t_user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(50) NOT NULL COMMENT '密码', `age` int DEFAULT NULL COMMENT '年龄', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

五、Groovy 与 Java 的差异对比

为了帮助开发者快速区分 Groovy 与 Java 的核心差异,以下是详细的对比表格,涵盖语法、特性、使用场景等关键维度,结合实战场景说明差异带来的开发效率影响:

特性GroovyJava核心差异说明与实战影响
变量定义支持 def 动态类型,可省略类型声明;静态类型也兼容必须显式指定类型(Java 10+ 支持 var 局部变量类型推断)Groovy 动态类型适合快速开发、脚本编写;Java 静态类型更严谨,适合大型项目类型安全控制
字符串支持 GString 插值(双引号、三引号);单引号为普通字符串仅支持字符串拼接(+)或 String.format,无插值功能Groovy 字符串插值大幅简化文本拼接,尤其适合 SQL 语句、日志打印等场景,减少代码冗余
集合定义与操作简化语法:List 用 [ ]、Map 用 [key:value];提供丰富高阶方法(each、findAll、collect 等)List/Map 需通过 new 关键字创建;高阶方法需借助 Stream API(Java 8+)Groovy 集合操作代码量仅为 Java 的 1/3 左右,无需手动处理迭代器,开发效率提升显著
方法定义与调用可省略返回类型、return 关键字;支持默认参数、命名参数;可省略方法调用括号必须显式指定返回类型;无默认参数(需重载);方法调用括号不可省略Groovy 方法语法更灵活,默认参数减少重载方法数量,命名参数提升多参数调用可读性
构造器自动生成 Map 构造器,支持new User(name: "xxx")语法需手动定义构造器或使用 Lombok 注解;仅支持按参数顺序传递Groovy 无需编写构造器即可快速创建对象,尤其适合多属性实体类初始化
闭包原生支持闭包,可作为参数、变量传递;支持委托机制无原生闭包,需通过 Lambda 表达式(Java 8+)替代,功能受限Groovy 闭包更灵活,支持上下文捕获、委托模式,适合回调、DSL 开发;Java Lambda 仅支持函数式接口
元编程支持 MetaClass、Expando 动态修改类行为、添加方法不支持运行时动态修改类结构,需借助字节码工具(如 ASM)Groovy 元编程适合 AOP 增强、动态扩展功能;Java 需额外依赖框架,实现复杂度高
异常处理可省略 checked 异常声明;支持多异常合并捕获必须声明或捕获 checked 异常;多异常捕获需 Java 7+ 支持Groovy 简化异常处理流程,适合脚本开发中快速迭代;Java 异常机制更严谨,适合企业级项目容错设计
文件操作提供简洁 API(如 file.text、file.eachLine),自动关闭资源需手动处理流的创建与关闭(或使用 try-with-resources,Java 7+)Groovy 一行代码即可完成文件读写,无需关注资源释放;Java 文件操作代码繁琐,易出现资源泄漏
空指针防护提供安全导航运算符(?.)、Elvis 运算符(?:)需手动判断 null(如 obj != null ? obj.method() : null)Groovy 空指针防护语法简洁,大幅减少空指针异常;Java 需编写大量判空代码,易遗漏
运算符重载支持自定义运算符重载(如 +、-、* 等)不支持自定义运算符重载,仅原生类型支持固定运算符Groovy 运算符重载适合领域模型开发(如金额、时间计算);Java 需通过方法实现类似功能
注释支持单行注释(//)、多行注释(/*/)、文档注释(/* */);额外支持 shebang 注释(#!)仅支持单行、多行、文档注释Groovy shebang 注释适合脚本文件直接运行(如 #!/usr/bin/env groovy),Java 无此功能
5.1 差异核心逻辑:动态 vs 静态

Groovy 作为动态语言,核心设计目标是“简化开发、提升效率”,通过动态类型推断、语法糖、原生特性(闭包、元编程)减少模板代码;而 Java 作为静态语言,核心目标是“类型安全、性能稳定”,通过严格的类型检查、编译期校验保障大型项目的可维护性。

两者底层均基于 JVM,字节码层面可无缝交互,因此实际开发中常采用“Groovy 负责快速开发脚本/工具类,Java 负责核心业务逻辑”的混合模式,兼顾效率与稳定性。

5.2 混合开发实战示例(Groovy + Java)

以下示例展示 Groovy 与 Java 混合开发的核心场景:Groovy 编写工具类(简化文件操作、集合处理),Java 编写核心业务服务,两者无缝调用。

5.2.1 Groovy 工具类(FileUtils.groovy)
/** * 文件操作工具类(Groovy 实现,简化 IO 操作) * @author ken */ package com.jam.demo.util import org.springframework.util.StringUtils class FileUtils { /** * 读取文件内容(自动关闭资源,支持编码指定) * @param filePath 文件路径 * @param charset 编码(默认 UTF-8) * @return 文件内容字符串 */ static String readFile(String filePath, String charset = "UTF-8") { if (!StringUtils.hasText(filePath)) { throw new IllegalArgumentException("文件路径不能为空") } def file = new File(filePath) if (!file.exists() || !file.isFile()) { throw new FileNotFoundException("文件不存在:${filePath}") } // Groovy 简化 API,自动关闭流 file.getText(charset) } /** * 按行处理文件(支持大文件,避免内存溢出) * @param filePath 文件路径 * @param closure 每行处理逻辑(闭包参数:行内容、行号) */ static void processFileByLine(String filePath, Closure closure) { if (!StringUtils.hasText(filePath) || ObjectUtils.isEmpty(closure)) { throw new IllegalArgumentException("文件路径或处理逻辑不能为空") } def file = new File(filePath) if (!file.exists() || !file.isFile()) { throw new FileNotFoundException("文件不存在:${filePath}") } // 按行读取,自动关闭流,闭包传递处理逻辑 file.eachLine { line, lineNum -> closure.call(line, lineNum) } } }
5.2.2 Java 业务服务类(UserBusinessService.java)
/** * 用户业务服务(Java 实现,核心业务逻辑) * @author ken */ package com.jam.demo.service; import com.jam.demo.entity.User; import com.jam.demo.mapper.UserMapper; import com.jam.demo.util.FileUtils; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import java.util.ArrayList; import java.util.List; @Service @Slf4j public class UserBusinessService extends ServiceImpl<UserMapper, User> { /** * 从文件批量导入用户(调用 Groovy 工具类实现文件读取) * @param filePath 用户数据文件路径(每行格式:用户名,密码,年龄) * @return 导入成功的用户数量 */ @Transactional(rollbackFor = Exception.class) public int batchImportUserFromFile(String filePath) { log.info("开始从文件导入用户:{}", filePath); List<User> userList = new ArrayList<>(); // 调用 Groovy 工具类的 processFileByLine 方法,传递 Java Lambda 作为闭包(Groovy 兼容 Java Lambda) FileUtils.processFileByLine(filePath, (line, lineNum) -> { log.debug("处理第 {} 行数据:{}", lineNum, line); if (ObjectUtils.isEmpty(line)) { log.warn("第 {} 行数据为空,跳过", lineNum); return; } // 解析行数据 String[] parts = line.split(","); if (parts.length != 3) { log.error("第 {} 行数据格式错误,跳过:{}", lineNum, line); return; } String username = parts[0].trim(); String password = parts[1].trim(); Integer age; try { age = Integer.parseInt(parts[2].trim()); } catch (NumberFormatException e) { log.error("第 {} 行年龄格式错误,跳过:{}", lineNum, line); return; } // 构建用户实体 User user = new User(); user.setUsername(username); user.setPassword(password); user.setAge(age); userList.add(user); }); // 批量保存用户 if (userList.isEmpty()) { log.warn("无有效用户数据可导入"); return 0; } boolean success = saveBatch(userList); log.info("用户导入完成,有效数据行数:{},导入成功:{}", userList.size(), success); return success ? userList.size() : 0; } }
5.2.3 测试验证(Groovy 测试类)
/** * 混合开发测试类 * @author ken */ package com.jam.demo.test import com.jam.demo.service.UserBusinessService import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest @SpringBootTest class MixedDevelopmentTest { @Autowired UserBusinessService userBusinessService def "测试从文件批量导入用户"() { given: "准备测试文件(user_import.txt),内容如下:" def testFile = new File("user_import.txt") testFile.text = """ zhangsan,123456,25 lisi,654321,30 wangwu,abc123,35 """ when: "调用导入方法" def importCount = userBusinessService.batchImportUserFromFile("user_import.txt") then: "验证导入结果" importCount == 3 def userList = userBusinessService.list() userList.size() >= 3 userList.any { it.username == "zhangsan" && it.age == 25 } } }
5.3 差异总结与选型建议
  1. 优先选 Groovy 的场景

    • 自动化脚本(部署脚本、数据迁移脚本、测试脚本)

    • 工具类开发(文件操作、数据格式转换、集合处理)

    • DSL 开发(领域特定语言,如 Gradle 脚本、Jenkins Pipeline)

    • 快速原型开发(需快速迭代验证业务逻辑)

  2. 优先选 Java 的场景

    • 大型企业级项目核心业务逻辑(需类型安全、可维护性)

    • 高性能要求的模块(静态类型编译优化,性能略优于动态类型)

    • 团队以 Java 开发为主,无需额外学习成本的场景

  3. 混合开发最佳实践

    • 用 Groovy 编写“辅助性”代码(工具类、脚本、测试用例),提升开发效率

    • 用 Java 编写“核心性”代码(业务服务、数据模型、接口定义),保障系统稳定

    • 借助 Spring 生态实现两者无缝集成,共享依赖与上下文

六、Groovy 实战场景:从脚本到企业级应用

6.1 场景 1:自动化测试脚本(结合 JUnit 5)

Groovy 简化的语法和丰富的断言方法,让测试脚本更简洁。以下示例实现用户模块的单元测试,结合 JUnit 5 和 AssertJ 断言:

/** * 用户服务单元测试 * @author ken */ package com.jam.demo.test import com.jam.demo.entity.User import com.jam.demo.service.UserService import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import static org.assertj.core.api.Assertions.assertThat @SpringBootTest class UserServiceTest { @Autowired UserService userService @Test void "测试新增用户"() { // 准备测试数据 def user = new User(username: "test_user", password: "test123", age: 28) // 执行测试方法 def savedUser = userService.addUser(user) // 断言结果 assertThat(savedUser).isNotNull() assertThat(savedUser.getId()).isGreaterThan(0) assertThat(savedUser.getUsername()).isEqualTo("test_user") assertThat(savedUser.getAge()).isEqualTo(28) } @Test void "测试根据ID查询用户"() { // 先新增用户 def user = new User(username: "query_user", password: "query123", age: 30) def savedUser = userService.addUser(user) // 查询用户 def queriedUser = userService.getUserById(savedUser.getId()) // 断言结果 assertThat(queriedUser).isNotNull() assertThat(queriedUser.getId()).isEqualTo(savedUser.getId()) assertThat(queriedUser.getUsername()).isEqualTo("query_user") } }
6.2 场景 2:数据迁移脚本(批量处理数据库数据)

利用 Groovy 简化的数据库操作和集合处理,实现从旧表到新表的数据迁移,结合 MyBatis-Plus 提升效率:

/** * 旧用户表到新用户表的数据迁移脚本 * @author ken */ package com.jam.demo.script import com.jam.demo.entity.OldUser import com.jam.demo.entity.User import com.jam.demo.mapper.OldUserMapper import com.jam.demo.mapper.UserMapper import com.jam.demo.util.DateUtils import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.CommandLineRunner import org.springframework.stereotype.Component import lombok.extern.slf4j.Slf4j import com.google.common.collect.Lists @Slf4j @Component class UserDataMigrationScript implements CommandLineRunner { @Autowired OldUserMapper oldUserMapper @Autowired UserMapper userMapper @Override void run(String... args) throws Exception { log.info("开始执行用户数据迁移脚本") // 1. 分页查询旧表数据(避免一次性加载过多数据导致内存溢出) int pageNum = 1 int pageSize = 1000 while (true) { // MyBatis-Plus 分页查询旧用户数据 def page = oldUserMapper.selectPage(new Page<>(pageNum, pageSize), null) if (page.getRecords().isEmpty()) { break } log.info("处理第 {} 页数据,共 {} 条", pageNum, page.getRecords().size()) // 2. 数据转换(旧表实体 -> 新表实体) def userList = page.getRecords().collect { OldUser oldUser -> new User( username: oldUser.getUserName(), // 字段名映射(旧表 userName -> 新表 username) password: oldUser.getPassword(), age: oldUser.getAge(), createTime: DateUtils.parse(oldUser.getCreateTimeStr(), "yyyy-MM-dd HH:mm:ss"), // 日期格式转换 updateTime: new Date() ) } // 3. 批量插入新表(分批处理,提升效率) Lists.partition(userList, 500).each { batch -> userMapper.insertBatchSomeColumn(batch) // MyBatis-Plus 批量插入方法 } pageNum++ } log.info("用户数据迁移脚本执行完成") } }
6.3 场景 3:Jenkins Pipeline 脚本(DevOps 自动化部署)

Groovy 是 Jenkins Pipeline 的默认脚本语言,利用其闭包和 DSL 特性,实现自动化构建、测试、部署流程:

/** * Jenkins Pipeline 自动化部署脚本 * @author ken */ pipeline { agent any // 任意可用的构建代理 environment { // 定义环境变量 PROJECT_NAME = 'groovy-demo' GIT_REPO = 'https://gitee.com/xxx/groovy-demo.git' BRANCH = 'master' JAR_PATH = 'target/groovy-demo-1.0-SNAPSHOT.jar' DEPLOY_PATH = '/opt/apps' } stages { // 阶段 1:拉取代码 stage('拉取代码') { steps { echo "开始拉取 ${GIT_REPO} 仓库 ${BRANCH} 分支代码" git url: GIT_REPO, branch: BRANCH } } // 阶段 2:编译构建 stage('编译构建') { steps { echo "开始编译构建项目" sh 'mvn clean package -Dmaven.test.skip=true' // 跳过测试编译 } post { success { echo "编译构建成功,生成 jar 包:${JAR_PATH}" } failure { echo "编译构建失败,请检查代码或依赖" error "编译构建失败,终止 Pipeline" } } } // 阶段 3:自动化测试 stage('自动化测试') { steps { echo "开始执行自动化测试" sh 'mvn test' } post { success { echo "自动化测试全部通过" } failure { echo "自动化测试存在失败用例,请修复后重新构建" error "自动化测试失败,终止 Pipeline" } } } // 阶段 4:部署到服务器 stage('部署到服务器') { steps { echo "开始部署项目到服务器" // 停止旧服务 sh "ssh root@192.168.1.100 'cd ${DEPLOY_PATH} && ./stop.sh'" // 上传新 jar 包 sh "scp ${JAR_PATH} root@192.168.1.100:${DEPLOY_PATH}" // 启动新服务 sh "ssh root@192.168.1.100 'cd ${DEPLOY_PATH} && ./start.sh'" } post { success { echo "项目部署成功!访问地址:http://192.168.1.100:8080" } failure { echo "项目部署失败,请检查服务器连接或启动脚本" } } } } // 流水线执行完成后操作 post { always { echo "Pipeline 执行完成,无论成功或失败都会执行" // 清理构建产物 sh 'mvn clean' } } }
6.4 场景 4:自定义 DSL(领域特定语言)

利用 Groovy 的闭包、委托机制,实现自定义 DSL,简化特定领域的配置或操作。以下示例实现一个简单的“数据同步任务”DSL:

/** * 数据同步任务 DSL 定义与使用示例 * @author ken */ package com.jam.demo.dsl import lombok.Data // 1. 定义 DSL 上下文类(存储 DSL 配置信息) @Data class SyncTask { String taskName String sourceTable String targetTable List<String> columns Closure whereClosure // 过滤条件闭包 Closure transformClosure // 数据转换闭包 } // 2. 定义 DSL 构建器(负责解析 DSL 语法,绑定闭包委托) class SyncTaskBuilder { SyncTask task = new SyncTask() // 任务名称配置 def name(String taskName) { task.setTaskName(taskName) } // 源表配置 def source(String sourceTable) { task.setSourceTable(sourceTable) } // 目标表配置 def target(String targetTable) { task.setTargetTable(targetTable) } // 同步字段配置 def columns(List<String> columns) { task.setColumns(columns) } // 过滤条件配置(闭包参数) def where(Closure closure) { task.setWhereClosure(closure) closure.delegate = task // 绑定闭包委托为 SyncTask closure.resolveStrategy = Closure.DELEGATE_FIRST // 优先从委托对象查找属性/方法 } // 数据转换配置(闭包参数) def transform(Closure closure) { task.setTransformClosure(closure) closure.delegate = task closure.resolveStrategy = Closure.DELEGATE_FIRST } // 构建 SyncTask 对象 SyncTask build() { // 校验必填配置 if (!task.getTaskName()) throw new IllegalArgumentException("任务名称不能为空") if (!task.getSourceTable()) throw new IllegalArgumentException("源表不能为空") if (!task.getTargetTable()) throw new IllegalArgumentException("目标表不能为空") if (task.getColumns().isEmpty()) throw new IllegalArgumentException("同步字段不能为空") return task } } // 3. 定义 DSL 入口方法(简化构建器创建) def syncTask(Closure closure) { def builder = new SyncTaskBuilder() closure.delegate = builder closure.resolveStrategy = Closure.DELEGATE_FIRST closure.call() return builder.build() } // 4. 使用 DSL 定义数据同步任务 def userSyncTask = syncTask { name "用户数据同步任务" source "t_old_user" target "t_user" columns ["id", "username", "age", "create_time"] where { // 过滤条件:年龄大于 18 且状态正常 "age > 18 AND status = 1" } transform { // 数据转换:将 create_time 格式化为 yyyy-MM-dd HH:mm:ss "DATE_FORMAT(create_time, '%Y-%m-%d %H:%i:%s') AS create_time" } } // 5. 执行数据同步任务(解析 DSL 配置,生成 SQL 并执行) class SyncTaskExecutor { static void execute(SyncTask task) { println "开始执行任务:${task.getTaskName()}" // 生成同步 SQL(基于 DSL 配置) def columnsStr = task.getColumns().join(", ") def whereClause = task.getWhereClosure().call() def transformClause = task.getTransformClosure().call() def syncSql = """ INSERT INTO ${task.getTargetTable()} (${columnsStr}) SELECT ${transformClause ?: columnsStr} FROM ${task.getSourceTable()} WHERE ${whereClause} """ println "生成同步 SQL:\n${syncSql}" // 执行 SQL(实际项目中结合 MyBatis/JDBC 执行) // jdbcTemplate.execute(syncSql) println "任务执行完成:${task.getTaskName()}" } } // 执行同步任务 SyncTaskExecutor.execute(userSyncTask)

七、Groovy 性能优化与最佳实践

7.1 性能优化关键点

Groovy 动态类型带来便利的同时,也可能导致性能损耗。以下是针对核心场景的优化方案,平衡开发效率与性能:

  1. 静态类型优化

    • 对热点代码(频繁调用的方法、循环)显式指定变量/方法的静态类型,避免动态类型推断开销

    • 启用 Groovy 静态编译注解@CompileStatic,将 Groovy 代码编译为静态类型字节码,性能接近 Java

    示例:

    import groovy.transform.CompileStatic /** * 静态编译优化示例 * @author ken */ @CompileStatic // 类级别静态编译 class PerformanceOptimizedService { // 显式指定静态类型 Integer calculateSum(List<Integer> list) { Integer sum = 0 for (Integer num : list) { // 静态类型循环,避免动态迭代开销 sum += num } return sum } }
  2. 集合操作优化

    • 避免在循环中频繁创建集合对象,优先使用Lists.newArrayList()预分配容量

    • 大集合处理优先使用each闭包(内部优化了迭代器),避免使用for in循环(动态类型迭代开销较大)

  3. 元编程性能优化

    • 动态添加方法/属性仅在初始化时执行,避免在热点代码中动态修改元类

    • 优先使用Expando而非动态修改已有类的元类,减少全局影响

  4. 文件操作优化

    • 大文件处理优先使用eachLine按行读取,避免text方法一次性加载全部内容到内存

    • 批量写入文件时,使用withWriter结合缓冲区,减少 IO 次数

7.2 最佳实践
  1. 代码规范

    • 动态类型与静态类型结合:工具类、脚本使用动态类型,核心业务逻辑使用静态类型(@CompileStatic

    • 方法命名遵循 Java 规范,变量命名使用驼峰命名法,提升代码可读性

    • 避免过度使用闭包嵌套,防止代码复杂度升高

  2. 依赖管理

    • 优先使用 Groovy 内置 API,避免重复引入第三方依赖

    • Groovy 版本与 JDK 版本匹配(Groovy 4.x 推荐 JDK 11+,本文示例基于 JDK 17)

  3. 异常处理

    • 明确异常边界,核心业务逻辑必须捕获特定异常,避免全局捕获Exception

    • 异常信息需包含上下文(如参数值、操作类型),便于问题定位

  4. 测试规范

    • 脚本类使用 Groovy 测试框架(Spock),企业级应用核心逻辑使用 JUnit 5

    • 测试用例需覆盖动态特性(如闭包、元编程),避免运行时异常

八、总结与学习资源

8.1 核心总结

Groovy 作为基于 JVM 的动态脚本语言,核心优势在于“简化开发、提升效率”,通过兼容 Java 语法、提供丰富语法糖(字符串插值、简化集合操作、闭包)、强大的元编程能力,成为脚本开发、工具类编写、DevOps 自动化的优选语言。

与 Java 混合开发时,可充分发挥两者优势:Java 保障核心业务的类型安全与稳定性,Groovy 提升辅助功能的开发效率。掌握 Groovy 的关键在于理解其动态特性与 Java 静态特性的差异,结合实战场景灵活选型。

8.2 权威学习资源
  1. 官方文档

    • Groovy 官方文档:最权威的语法、特性说明,包含大量示例代码

    • Groovy 官方指南:入门到进阶的系统学习指南

  2. 实战书籍

    • 《Groovy in Action》:Groovy 领域经典书籍,深入讲解核心特性与实战场景

    • 《Programming Groovy 2》:适合 Java 开发者快速上手的入门书籍

  3. 工具与生态

    • Groovy 在线编辑器:无需本地环境,直接运行 Groovy 代码

    • Spock 测试框架:基于 Groovy 的测试框架,支持行为驱动开发(BDD)

    • Gradle 文档:Gradle 构建脚本基于 Groovy,学习 Gradle 可深化 Groovy 应用

  4. 社区资源

    • Groovy GitHub 仓库:源码与 issue 讨论

    • Stack Overflow Groovy 标签:问题解决与经验分享

通过本文的系统讲解与实战示例,相信开发者已能掌握 Groovy 核心语法与应用场景。建议结合实际项目需求,从脚本编写、工具类开发入手,逐步深入元编程、DSL 等高级特性,充分发挥 Groovy 提升开发效率的优势。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/24 9:25:34

EmotiVoice技术深度解析:为何它成为情感化语音合成的新标杆?

EmotiVoice技术深度解析&#xff1a;为何它成为情感化语音合成的新标杆&#xff1f; 在虚拟偶像直播中突然笑出眼泪&#xff0c;或是在AI客服电话里听出一丝“不耐烦”——这些曾经只属于人类的微妙情绪表达&#xff0c;如今正被一种名为 EmotiVoice 的开源语音合成系统悄然实现…

作者头像 李华
网站建设 2026/2/26 16:45:17

高版本springboot lombok失效解决方案

1.idea问题 POM <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.…

作者头像 李华
网站建设 2026/2/24 9:10:14

微信机器人开发文档

微信机器人开发文档 作为专注微信生态开发的高阶API封装平台&#xff0c;WTAPI框架凭借深度协议解析与RPA流程自动化技术&#xff0c;已实现微信从个人号到社群、朋友圈的全链路功能覆盖。无论是营销客服、用户运营还是数据管理&#xff0c;开发者均可通过简洁的API调用&#…

作者头像 李华
网站建设 2026/2/25 10:32:38

2026年会“马上有钱”接金币游戏

2026马年将至&#xff0c;年会作为企业凝聚团队、辞旧迎新的核心场景&#xff0c;既需要传递“钱”途无量的美好期许&#xff0c;更渴望打破传统冷场困局&#xff0c;实现全员深度参与。熹乐互动深耕年会互动技术领域&#xff0c;重磅推出2026年“马上有钱”接金币游戏&#xf…

作者头像 李华
网站建设 2026/2/27 23:50:08

20、Mac OS X 库使用与开发指南

Mac OS X 库使用与开发指南 1. 特定库的版本差异及处理 在Mac OS X系统中,不同版本对一些库的使用有所不同: - curses库 :在Mac OS X 10.1及更早版本中,curses屏幕库是libSystem.dylib的一部分。而在Mac OS X 10.2及更高版本中,使用ncurses库(/usr/lib/libncurses.5…

作者头像 李华
网站建设 2026/2/26 2:53:04

22、Mac系统下Fink与MacPorts的安装与使用指南

Mac系统下Fink与MacPorts的安装与使用指南 1. Fink的安装与使用 1.1 安装前准备 在安装Fink之前,需要确保X11和X11 SDK已安装。X11在Leopard系统中默认安装,而在早期的Mac OS X版本中是可选安装项,X11 SDK包含在Xcode中。 1.2 Fink的安装方式 Fink可以通过二进制文件、…

作者头像 李华