首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Groovy 脚本语法全解析:从入门到精通的干货指南

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

作者头像
果酱带你啃java
发布2026-04-14 14:08:53
发布2026-04-14 14:08:53
470
举报

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):

代码语言:javascript
复制
<?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 基本语法
代码语言:javascript
复制
/**
 * 变量定义示例
 * @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类似,分为局部变量、成员变量、静态变量:

代码语言:javascript
复制
/**
 * 变量作用域示例
 * @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完全兼容)
代码语言:javascript
复制
/**
 * 基本数据类型示例
 * @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的字符串拼接更简洁。
代码语言:javascript
复制
/**
 * 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(左闭右开区间)。
代码语言:javascript
复制
/**
 * 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一致)
代码语言:javascript
复制
/**
 * 基础运算符示例
 * @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,而非抛出异常。
代码语言:javascript
复制
/**
 * 安全导航运算符示例
 * @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 : 默认值
代码语言:javascript
复制
/**
 * 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。
代码语言:javascript
复制
/**
 * 安全类型转换运算符示例
 * @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
  • 展开运算符(*):用于集合或数组的展开,将集合元素作为独立参数传递给方法。
代码语言:javascript
复制
/**
 * 展开运算符示例
 * @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一致)
代码语言:javascript
复制
/**
 * 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循环(扩展了更简洁的语法)
代码语言:javascript
复制
/**
 * 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 (defitem : list) {
    println("增强循环:${item}")
}

// 3. Groovy简化循环(遍历Range)
for (i in1..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一致)
代码语言:javascript
复制
/**
 * 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更强大。

代码语言:javascript
复制
/**
 * switch-case示例
 * @author ken
 */
def obj = "Groovy"

switch (obj) {
    case1:
        println("整数1")
        break
    case"Groovy":
        println("字符串:Groovy")
        break
    case [1, 2, 3]:
        println("集合中的元素")
        break
    caseInteger:
        println("Integer类型")
        break
    caseString:
        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, ...]

代码语言:javascript
复制
/**
 * 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, ...],键默认是字符串(可省略引号)。

代码语言:javascript
复制
/**
 * 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, ...])

代码语言:javascript
复制
/**
 * 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 基础方法定义
代码语言:javascript
复制
/**
 * 基础方法定义示例
 * @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支持为方法参数设置默认值,调用时可省略默认参数。

代码语言:javascript
复制
/**
 * 可选参数与默认参数示例
 * @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类似,使用...表示变长参数,变长参数必须位于方法参数列表的最后。

代码语言:javascript
复制
/**
 * 变长参数示例
 * @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 方法调用省略括号

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

代码语言:javascript
复制
/**
 * 方法调用省略括号示例
 * @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方法,无需手动编写。

代码语言:javascript
复制
/**
 * 简化类定义示例
 * @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构造器,也可手动定义构造器。

代码语言:javascript
复制
/**
 * 构造器示例
 * @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)。

代码语言:javascript
复制
/**
 * 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参数(代表闭包的唯一参数)。

代码语言:javascript
复制
/**
 * 闭包基础示例
 * @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表达式,但更灵活)。

代码语言:javascript
复制
/**
 * 闭包作为方法参数示例
 * @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 闭包捕获上下文变量

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

代码语言:javascript
复制
/**
 * 闭包捕获上下文变量示例
 * @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一致,可手动修改,用于实现委托模式。
代码语言:javascript
复制
/**
 * 闭包委托示例
 * @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允许动态创建类和添加属性、方法,适合快速构建临时对象。

代码语言:javascript
复制
/**
 * 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),通过元类可以动态添加静态方法、实例方法,或修改现有方法的行为。

代码语言:javascript
复制
/**
 * 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、抛出异常,但语法更简洁,且支持多异常捕获。

代码语言:javascript
复制
/**
 * 异常处理示例
 * @author ken
 */
def divide(Integer a, Integer b) {
    if (b == 0) {
        // 抛出异常(与Java一致)
        thrownew 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 读取文件
代码语言:javascript
复制
/**
 * 文件读取示例
 * @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 写入文件
代码语言:javascript
复制
/**
 * 文件写入示例
 * @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)
代码语言:javascript
复制
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)
代码语言:javascript
复制
/**
 * 用户实体类
 * @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)
代码语言:javascript
复制
/**
 * 用户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)
代码语言:javascript
复制
/**
 * 用户服务类
 * @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)
代码语言:javascript
复制
/**
 * 用户控制器
 * @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())) {
            thrownew 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)
代码语言:javascript
复制
/**
 * 启动类
 * @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)
代码语言:javascript
复制
CREATE TABLE`t_user` (
`id`intNOTNULL AUTO_INCREMENT COMMENT'主键ID',
`username`varchar(50) NOTNULLCOMMENT'用户名',
`password`varchar(50) NOTNULLCOMMENT'密码',
`age`intDEFAULTNULLCOMMENT'年龄',
`create_time` datetime DEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',
`update_time` datetime DEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMPCOMMENT'更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COMMENT='用户表';

五、Groovy 与 Java 的差异对比

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

特性

Groovy

Java

核心差异说明与实战影响

变量定义

支持 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)
代码语言:javascript
复制
/**
 * 文件操作工具类(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)) {
            thrownew IllegalArgumentException("文件路径不能为空")
        }
        def file = new File(filePath)
        if (!file.exists() || !file.isFile()) {
            thrownew FileNotFoundException("文件不存在:${filePath}")
        }
        // Groovy 简化 API,自动关闭流
        file.getText(charset)
    }

    /**
     * 按行处理文件(支持大文件,避免内存溢出)
     * @param filePath 文件路径
     * @param closure 每行处理逻辑(闭包参数:行内容、行号)
     */
    staticvoid processFileByLine(String filePath, Closure closure) {
        if (!StringUtils.hasText(filePath) || ObjectUtils.isEmpty(closure)) {
            thrownew IllegalArgumentException("文件路径或处理逻辑不能为空")
        }
        def file = new File(filePath)
        if (!file.exists() || !file.isFile()) {
            thrownew FileNotFoundException("文件不存在:${filePath}")
        }
        // 按行读取,自动关闭流,闭包传递处理逻辑
        file.eachLine { line, lineNum ->
            closure.call(line, lineNum)
        }
    }
}
5.2.2 Java 业务服务类(UserBusinessService.java)
代码语言:javascript
复制
/**
 * 用户业务服务(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
publicclass 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("无有效用户数据可导入");
            return0;
        }
        boolean success = saveBatch(userList);
        log.info("用户导入完成,有效数据行数:{},导入成功:{}", userList.size(), success);
        return success ? userList.size() : 0;
    }
}
5.2.3 测试验证(Groovy 测试类)
代码语言:javascript
复制
/**
 * 混合开发测试类
 * @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 断言:

代码语言:javascript
复制
/**
 * 用户服务单元测试
 * @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
importstatic 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 提升效率:

代码语言:javascript
复制
/**
 * 旧用户表到新用户表的数据迁移脚本
 * @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 特性,实现自动化构建、测试、部署流程:

代码语言:javascript
复制
/**
 * 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:

代码语言:javascript
复制
/**
 * 数据同步任务 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()) thrownew IllegalArgumentException("任务名称不能为空")
        if (!task.getSourceTable()) thrownew IllegalArgumentException("源表不能为空")
        if (!task.getTargetTable()) thrownew IllegalArgumentException("目标表不能为空")
        if (task.getColumns().isEmpty()) thrownew 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 {
    staticvoid 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 动态类型带来便利的同时,也可能导致性能损耗。以下是针对核心场景的优化方案,平衡开发效率与性能:

静态类型优化

示例:

代码语言:javascript
复制
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
    }
}
  • 对热点代码(频繁调用的方法、循环)显式指定变量/方法的静态类型,避免动态类型推断开销
  • 启用 Groovy 静态编译注解 @CompileStatic,将 Groovy 代码编译为静态类型字节码,性能接近 Java

集合操作优化

  • 避免在循环中频繁创建集合对象,优先使用 Lists.newArrayList() 预分配容量
  • 大集合处理优先使用 each 闭包(内部优化了迭代器),避免使用 for in 循环(动态类型迭代开销较大)

元编程性能优化

  • 动态添加方法/属性仅在初始化时执行,避免在热点代码中动态修改元类
  • 优先使用 Expando 而非动态修改已有类的元类,减少全局影响

文件操作优化

  • 大文件处理优先使用 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 提升开发效率的优势。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 果酱带你啃java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Groovy作为一门基于JVM的动态编程语言,兼具Java的稳定性与脚本语言的灵活性,在自动化测试、DevOps、数据处理等领域被广泛应用。本文将从基础语法到高级特性,结合大量可直接编译运行的实例,深入剖析Groovy脚本的核心逻辑与实战技巧,帮助开发者快速掌握这门高效的编程语言。
    • 一、Groovy简介与环境搭建
      • 1.1 什么是Groovy?
      • 1.2 环境搭建(Maven方式)
    • 二、Groovy基础语法(兼容Java,更简洁)
      • 2.1 变量定义
      • 2.2 数据类型
      • 2.3 运算符
      • 2.4 流程控制语句
    • 三、Groovy核心特性(相比Java的优势)
      • 3.1 集合操作(List、Map、Set)
      • 3.2 方法定义与调用
      • 3.3 类与对象
      • 3.4 闭包(Closure)
    • 四、Groovy高级特性
      • 4.1 元编程(Metaprogramming)
      • 4.2 异常处理
      • 4.3 文件操作
      • 4.4 数据库操作(结合MyBatis-Plus)
  • 五、Groovy 与 Java 的差异对比
    • 5.1 差异核心逻辑:动态 vs 静态
    • 5.2 混合开发实战示例(Groovy + Java)
      • 5.2.1 Groovy 工具类(FileUtils.groovy)
      • 5.2.2 Java 业务服务类(UserBusinessService.java)
      • 5.2.3 测试验证(Groovy 测试类)
    • 5.3 差异总结与选型建议
  • 六、Groovy 实战场景:从脚本到企业级应用
    • 6.1 场景 1:自动化测试脚本(结合 JUnit 5)
    • 6.2 场景 2:数据迁移脚本(批量处理数据库数据)
    • 6.3 场景 3:Jenkins Pipeline 脚本(DevOps 自动化部署)
    • 6.4 场景 4:自定义 DSL(领域特定语言)
  • 七、Groovy 性能优化与最佳实践
    • 7.1 性能优化关键点
    • 7.2 最佳实践
  • 八、总结与学习资源
    • 8.1 核心总结
    • 8.2 权威学习资源
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档