Groovy 作为一门基于 JVM 的动态编程语言,兼具 Java 的稳定性与脚本语言的灵活性,在自动化测试、DevOps、数据处理等领域应用广泛。本文按“基础语法 → 核心特性 → 高级能力 → 实战与优化”的路径,配合可直接编译运行的示例,帮助你快速建立 Groovy 的知识体系并落地到项目中。
参考文档:Groovy 官方文档:https://groovy-lang.org/documentation.html
一、Groovy 简介与环境搭建
1.1 什么是 Groovy?
Groovy 是基于 JVM 的动态脚本语言,由 James Strachan 于 2003 年创建。它完全兼容 Java 语法,同时吸收了 Python、Ruby 等语言的表达优势:更简洁的语法、更强的元编程能力、更丰富的内置 API。Groovy 代码可直接编译为 Java 字节码,无缝运行在 JVM 上,并能与 Java 类库互操作(适合在现有 Java 生态中渐进式引入)。
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 等更贴近脚本/DSL 的类型。
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)
// 验证类型
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) }
println("整数左闭右开区间遍历:")
intRangeOpen.each { println(it) }
println("字符范围遍历:")
charRange.each { println(it) }
// Range常用方法
println("intRange是否包含3:${intRange.contains(3)}")
println("intRange的长度:${intRange.size()}")
println("intRange的第一个元素:${intRange.from}")
println("intRange的最后一个元素:${intRange.to}")
2.3 运算符
Groovy 支持 Java 的所有运算符,并补充了不少常用“语法糖”。
2.3.1 基础运算符(与 Java 一致)
/**
* 基础运算符示例
* @author ken
*/
def a = 10
def b = 3
// 算术运算符
println("a + b = ${a + b}")
println("a - b = ${a - b}")
println("a * b = ${a * b}")
println("a / b = ${a / b}")
println("a % b = ${a % b}")
println("a ** b = ${a ** b}") // 幂运算,Groovy扩展
// 比较运算符
println("a == b ? ${a == b}")
println("a > b ? ${a > b}")
println("a < b ? ${a < b}")
// 逻辑运算符
def flag1 = true
def flag2 = false
println("flag1 && flag2 = ${flag1 && flag2}")
println("flag1 || flag2 = ${flag1 || flag2}")
println("!flag1 = ${!flag1}")
2.3.2 Groovy 扩展运算符
- 安全导航运算符(
?.):对象为 null 时直接返回 null,避免 NPE。
/**
* 安全导航运算符示例
* @author ken
*/
class User {
String name
Integer age
}
def user1 = new User(name: "张三", age: 25)
def user2 = null
println("user1姓名:${user1?.name}")
println("user2姓名:${user2?.name}")
- 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"
def num1 = obj1 as? Integer
println("num1类型:${num1.class},值:${num1}")
def num2 = obj2 as? Integer
println("num2:${num2}")
- *展开运算符(``)**:把集合元素“展开”为独立参数。
/**
* 展开运算符示例
* @author ken
*/
def list = [1, 2, 3]
// 定义一个接收多个参数的方法
def sum(a, b, c) {
return a + b + c
}
// 使用展开运算符传递集合元素
def total = sum(*list)
println("总和:${total}")
// 字符串展开(转换为字符数组)
def str = "abc"
def charList = [*str]
println(charList)
2.4 流程控制语句
Groovy 的流程控制与 Java 基本一致,但在遍历 Range、集合、Map 等场景更简洁。
2.4.1 if-else
/**
* 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
/**
* 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(更灵活)
/**
* 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(列表)
/**
* List操作示例
* @author ken
*/
// 定义List(默认ArrayList)
def list = [1, 2, 3, 4, 5]
// 1. 访问元素
println("第一个元素:${list[0]}")
println("最后一个元素:${list[-1]}") // 负索引:从尾部开始
println("索引1到3的元素:${list[1..3]}") // Range切片
// 2. 添加元素
list.add(6)
list << 7
list.addAll([8, 9])
println("添加元素后:${list}")
// 3. 删除元素
list.remove(0)
list.remove(Integer.valueOf(9))
list -= 8
println("删除元素后:${list}")
// 4. 遍历List
println("each遍历:")
list.each { element ->
println(element)
}
println("eachWithIndex遍历:")
list.eachWithIndex { element, index ->
println("索引${index}:${element}")
}
// 5. 过滤与转换
def evenList = list.findAll { it % 2 == 0 }
println("偶数列表:${evenList}")
def doubleList = list.collect { it * 2 }
println("元素翻倍后:${doubleList}")
// 6. 聚合操作
def sum = list.sum()
def max = list.max()
def min = list.min()
def average = list.average()
println("求和:${sum},最大值:${max},最小值:${min},平均值:${average}")
3.1.2 Map(映射)
/**
* Map操作示例
* @author ken
*/
def map = [name: "Groovy", version: "4.0.20", author: "James Strachan"]
// 1. 访问元素
println("name:${map.name}")
println("version:${map['version']}")
println("author:${map.get('author')}")
println("不存在的键:${map.get('desc', '无描述')}")
// 2. 添加元素
map.desc = "基于JVM的动态脚本语言"
map.put("year", 2003)
map << [language: "Java"]
println("添加元素后:${map}")
// 3. 删除元素
map.remove("year")
map -= "language"
println("删除元素后:${map}")
// 4. 遍历Map
map.each { entry ->
println("键:${entry.key},值:${entry.value}")
}
map.each { key, value ->
println("键:${key},值:${value}")
}
println("遍历键:")
map.keySet().each { println(it) }
println("遍历值:")
map.values().each { println(it) }
// 5. 过滤与转换
def filteredMap = map.findAll { key, value ->
value.toString().length() > 5
}
println("筛选后:${filteredMap}")
def entryList = map.collect { key, value ->
[key: key, value: value]
}
println("转换为List:${entryList}")
3.1.3 Set(集合)
/**
* Set操作示例
* @author ken
*/
def set = [1, 2, 2, 3, 3, 3] as Set
println("Set初始化(去重):${set}")
set.add(4)
set << 5
println("添加元素后:${set}")
set.remove(2)
set -= 3
println("删除元素后:${set}")
def set1 = [1, 2, 3, 4] as Set
def set2 = [3, 4, 5, 6] as Set
def union = set1 + set2
println("并集:${union}")
def intersection = set1.intersect(set2)
println("交集:${intersection}")
def difference = set1 - set2
println("差集:${difference}")
set1.each { println(it) }
3.2 方法定义与调用
Groovy 方法更“短平快”:可省略返回类型和 return,支持默认参数、命名参数、变长参数等。
3.2.1 基础方法定义
/**
* 基础方法定义示例
* @author ken
*/
def printHello(String name) {
println("Hello, ${name}!")
}
def add(Integer a, Integer b) {
a + b
}
Integer multiply(Integer a, Integer b) {
return a * b
}
printHello("Groovy")
def sum = add(10, 20)
println("10 + 20 = ${sum}")
def product = multiply(10, 20)
println("10 * 20 = ${product}")
3.2.2 可选参数与默认参数
/**
* 可选参数与默认参数示例
* @author ken
*/
def sayHello(String name, String prefix = "Hello", String suffix = "!") {
"${prefix}, ${name}${suffix}"
}
def msg1 = sayHello("张三", "Hi", "!!!")
println(msg1)
def msg2 = sayHello("李四")
println(msg2)
def msg3 = sayHello(suffix: "~", name: "王五", prefix: "Hi")
println(msg3)
3.2.3 变长参数
/**
* 变长参数示例
* @author ken
*/
def sumAll(Integer... args) {
args.sum()
}
def total1 = sumAll(1, 2, 3, 4, 5)
println("总和1:${total1}")
def nums = [6, 7, 8]
def total2 = sumAll(*nums)
println("总和2:${total2}")
3.2.4 方法调用省略括号
/**
* 方法调用省略括号示例
* @author ken
*/
def printMsg(String msg) {
println(msg)
}
printMsg "Hello, Groovy!"
def appendStr(String str1, String str2) {
str1 + str2
}
def result = appendStr "Hello, ", "Groovy"
println result
3.3 类与对象
Groovy 类定义兼容 Java,同时提供了更顺手的属性访问与构造方式。
3.3.1 简化的类定义
/**
* 简化类定义示例
* @author ken
*/
class User {
String name
Integer age
String email
}
def user = new User(name: "张三", age: 25, email: "zhangsan@example.com")
println("姓名:${user.name}")
println("年龄:${user.age}")
user.age = 26
println("修改后的年龄:${user.age}")
3.3.2 构造器
/**
* 构造器示例
* @author ken
*/
class User {
String name
Integer age
User(String name, Integer age) {
this.name = name
this.age = age
}
@Override
String toString() {
"User{name='${name}', age=${age}}"
}
}
def user1 = new User("李四", 30)
println(user1)
class User2 {
String name
Integer age
User2() {}
@Override
String toString() {
"User2{name='${name}', age=${age}}"
}
}
def user2 = new User2(name: "王五", age: 35)
println(user2)
3.3.3 Groovy 注解简化类定义
/**
* Groovy注解简化类定义示例
* @author ken
*/
import groovy.transform.ToString
import groovy.transform.EqualsAndHashCode
import groovy.transform.TupleConstructor
import groovy.transform.Data
@ToString
class User3 {
String name
Integer age
}
def user3 = new User3("赵六", 40)
println(user3)
@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}")
println("user4_1 == user4_3 ? ${user4_1 == user4_3}")
@TupleConstructor
class User5 {
String name
Integer age
}
def user5 = new User5("吴九", 55)
println(user5.name)
println(user5.age)
@Data
class User6 {
String name
Integer age
}
def user6 = new User6("郑十", 60)
println(user6)
def user6_2 = new User6("郑十", 60)
println("user6 == user6_2 ? ${user6 == user6_2}")
3.4 闭包(Closure)
闭包是 Groovy 的核心能力之一:可执行代码块,可捕获上下文,可作为参数传递,是构建 DSL、回调逻辑的基础。
3.4.1 闭包基础定义与调用
/**
* 闭包基础示例
* @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")
3.4.2 闭包作为方法参数
/**
* 闭包作为方法参数示例
* @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}")
}
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}")
3.4.4 闭包的委托(Delegate)
/**
* 闭包委托示例
* @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()
class AnotherDelegate {
String name = "AnotherDelegate"
}
def demo = new DelegateDemo()
def anotherDelegate = new AnotherDelegate()
def closure = {
println("delegate:${delegate.name}")
}
closure.delegate = anotherDelegate
closure()
四、Groovy 高级特性
Groovy 支持运行时动态修改类行为、添加方法、拦截方法调用等能力(MetaClass、Expando 等),适合 AOP 增强、DSL 框架、快速扩展。
4.1.1 Expando(动态类)
/**
* Expando示例
* @author ken
*/
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}")
user.sayHello()
def sum = user.calculate(10, 20)
println("10 + 20 = ${sum}")
/**
* MetaClass示例
* @author ken
*/
class User {
String name
Integer age
}
User.metaClass.sayHello = {
println("Hello, ${name}! 年龄:${age}")
}
User.metaClass.static.createUser = { String name, Integer age ->
new User(name: name, age: age)
}
def user = User.createUser("李四", 30)
user.sayHello()
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()
4.2 异常处理
Groovy 异常处理与 Java 基本一致,支持 try-catch-finally,也支持多异常合并捕获(Groovy 3.0+)。
/**
* 异常处理示例
* @author ken
*/
def divide(Integer a, Integer b) {
if (b == 0) {
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("异常处理结束")
}
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 更贴近脚本场景,读写更简短,资源管理更省心。结合自动化脚本、构建任务、运维工具时尤其顺手(可扩展到 运维/DevOps 的常见场景)。
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,实现常规 CRUD 与批处理能力。下面是基于 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 |
核心差异说明与实战影响 |
| 变量定义 |
支持 def 动态类型;静态类型也兼容 |
必须显式类型(Java 10+ 支持 var) |
Groovy 更适合脚本/快速开发;Java 更适合强类型的大型工程 |
| 字符串 |
GString 插值(双引号、三引号) |
仅拼接或 String.format |
Groovy 拼接日志、SQL 文本更省代码 |
| 集合操作 |
[ ]、[k:v],内建 each/findAll/collect |
new 创建,常用 Stream(Java 8+) |
Groovy 集合处理更短,适合数据处理脚本 |
| 方法定义 |
可省略返回类型/return;默认参数、命名参数 |
返回类型必须;默认参数需重载 |
Groovy 可减少大量模板方法 |
| 构造器 |
默认 Map 构造器 new User(name:"x") |
通常手写或 Lombok |
Groovy 适合实体初始化与 DSL |
| 闭包 |
原生 Closure,支持委托 |
Lambda 需函数式接口 |
Groovy 更适合 DSL/回调/配置式编程 |
| 元编程 |
MetaClass/Expando 运行时增强 |
不支持(需字节码工具) |
Groovy 动态扩展强,但需控制范围避免影响全局 |
| 异常处理 |
可省略 checked 异常声明;多异常捕获 |
checked 异常必须声明/捕获 |
Groovy 更利于脚本迭代;Java 更严格 |
| 文件操作 |
file.text/eachLine 简洁,自动管理资源 |
IO 代码相对繁琐 |
Groovy 用于脚本/工具类非常高效 |
| 空指针防护 |
?.、?: |
需手动判空 |
Groovy 判空更自然,减少 NPE 风险 |
| 运算符重载 |
支持 |
不支持 |
Groovy 在领域模型表达上更灵活 |
| 注释 |
额外支持 shebang #! |
无 |
Groovy 脚本可直接运行(类 Unix 环境) |
5.1 差异核心逻辑:动态 vs 静态
Groovy 的设计目标更偏向“减少样板代码、提升开发效率”;Java 更强调“类型安全、长期可维护性”。两者同在 JVM 上运行,字节码层面天然可互操作,因此工程实践中常见模式是:Groovy 负责脚本/工具/测试,Java 负责核心业务与接口契约。
5.2 混合开发实战示例(Groovy + Java)
下面展示一种典型搭配:Groovy 实现文件工具类,Java 业务服务调用 Groovy 工具完成批量导入。
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 差异总结与选型建议
-
优先选 Groovy 的场景
- 自动化脚本(部署脚本、数据迁移脚本、测试脚本)
- 工具类开发(文件操作、数据格式转换、集合处理)
- DSL 开发(Gradle 脚本、Jenkins Pipeline)
- 快速原型验证(迭代速度优先)
-
优先选 Java 的场景
- 核心业务逻辑(强调类型安全与可维护性)
- 强性能诉求模块(静态编译更易优化)
- 团队以 Java 为主、希望降低额外学习成本
-
混合开发建议
- Groovy 写“辅助性”代码:工具、脚本、测试
- Java 写“核心性”代码:接口、模型、业务服务
- 依托 Spring 生态共享依赖与上下文,实现无缝协作
六、Groovy 实战场景:从脚本到企业级应用
6.1 场景 1:自动化测试脚本(结合 JUnit 5)
/**
* 用户服务单元测试
* @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:数据迁移脚本(批量处理数据库数据)
/**
* 旧用户表到新用户表的数据迁移脚本
* @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("开始执行用户数据迁移脚本")
int pageNum = 1
int pageSize = 1000
while (true) {
def page = oldUserMapper.selectPage(new Page<>(pageNum, pageSize), null)
if (page.getRecords().isEmpty()) {
break
}
log.info("处理第 {} 页数据,共 {} 条", pageNum, page.getRecords().size())
def userList = page.getRecords().collect { OldUser oldUser ->
new User(
username: oldUser.getUserName(),
password: oldUser.getPassword(),
age: oldUser.getAge(),
createTime: DateUtils.parse(oldUser.getCreateTimeStr(), "yyyy-MM-dd HH:mm:ss"),
updateTime: new Date()
)
}
Lists.partition(userList, 500).each { batch ->
userMapper.insertBatchSomeColumn(batch)
}
pageNum++
}
log.info("用户数据迁移脚本执行完成")
}
}
6.3 场景 3:Jenkins Pipeline 脚本(DevOps 自动化部署)
/**
* 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 {
stage('拉取代码') {
steps {
echo "开始拉取 ${GIT_REPO} 仓库 ${BRANCH} 分支代码"
git url: GIT_REPO, branch: BRANCH
}
}
stage('编译构建') {
steps {
echo "开始编译构建项目"
sh 'mvn clean package -Dmaven.test.skip=true'
}
post {
success {
echo "编译构建成功,生成 jar 包:${JAR_PATH}"
}
failure {
echo "编译构建失败,请检查代码或依赖"
error "编译构建失败,终止 Pipeline"
}
}
}
stage('自动化测试') {
steps {
echo "开始执行自动化测试"
sh 'mvn test'
}
post {
success {
echo "自动化测试全部通过"
}
failure {
echo "自动化测试存在失败用例,请修复后重新构建"
error "自动化测试失败,终止 Pipeline"
}
}
}
stage('部署到服务器') {
steps {
echo "开始部署项目到服务器"
sh "ssh root@192.168.1.100 'cd ${DEPLOY_PATH} && ./stop.sh'"
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(领域特定语言)
/**
* 数据同步任务 DSL 定义与使用示例
* @author ken
*/
package com.jam.demo.dsl
import lombok.Data
@Data
class SyncTask {
String taskName
String sourceTable
String targetTable
List<String> columns
Closure whereClosure
Closure transformClosure
}
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
closure.resolveStrategy = Closure.DELEGATE_FIRST
}
def transform(Closure closure) {
task.setTransformClosure(closure)
closure.delegate = task
closure.resolveStrategy = Closure.DELEGATE_FIRST
}
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
}
}
def syncTask(Closure closure) {
def builder = new SyncTaskBuilder()
closure.delegate = builder
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.call()
return builder.build()
}
def userSyncTask = syncTask {
name "用户数据同步任务"
source "t_old_user"
target "t_user"
columns ["id", "username", "age", "create_time"]
where {
"age > 18 AND status = 1"
}
transform {
"DATE_FORMAT(create_time, '%Y-%m-%d %H:%i:%s') AS create_time"
}
}
class SyncTaskExecutor {
static void execute(SyncTask task) {
println "开始执行任务:${task.getTaskName()}"
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}"
// jdbcTemplate.execute(syncSql)
println "任务执行完成:${task.getTaskName()}"
}
}
SyncTaskExecutor.execute(userSyncTask)
七、Groovy 性能优化与最佳实践
7.1 性能优化关键点
Groovy 的动态特性提升了表达力,但在热点路径上可能有额外开销。常见优化策略:
- 静态类型优化
- 热点方法/循环显式指定类型
- 使用
@CompileStatic 将代码静态编译,性能更接近 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
}
}
-
集合操作优化
- 避免在循环中频繁创建临时集合
- 大集合处理关注分批与流式处理,减少一次性装载与中间对象
-
元编程优化
- 动态增强应尽量放在初始化阶段
- 避免在热点代码里频繁修改
MetaClass(影响范围大、难排查)
-
文件操作优化
- 大文件优先
eachLine,避免 text 一次性读入内存
- 写入用
withWriter 减少 IO 次数
7.2 最佳实践
-
代码规范
- 工具/脚本可动态类型;核心业务建议静态类型或
@CompileStatic
- 命名与结构尽量对齐 Java 工程习惯,提升协作与维护性
- 避免闭包过度嵌套,控制复杂度
-
依赖管理
- 优先使用 Groovy 内建 API,避免重复引第三方
- 注意 Groovy 与 JDK 版本匹配(Groovy 4.x 推荐 JDK 11+;本文示例基于 JDK 17)
-
异常处理
- 核心业务优先捕获特定异常,避免无脑兜底
Exception
- 异常信息带上下文(参数、操作、环境),利于定位
-
测试规范
- 脚本/DSL 可用 Spock;企业应用核心逻辑建议 JUnit 5
- 覆盖动态特性(闭包委托、元编程扩展点),减少运行时意外
八、总结与学习资源
8.1 核心总结
Groovy 的优势在于“更少的样板代码 + 更强的表达能力”:字符串插值、集合高阶操作、闭包与 DSL、元编程能力,让它在脚本、工具、测试、DevOps 自动化领域非常高效。与 Java 混合使用时,建议把 Groovy 放在“效率优先”的外围层,把 Java 放在“稳定与契约优先”的核心层,从而兼顾迭代速度与系统稳定性。
8.2 权威学习资源