
Groovy是一门基于JVM的动态脚本语言,由James Strachan于2003年创建。它完全兼容Java语法,同时吸收了Python、Ruby等脚本语言的优点,提供了更简洁的语法、强大的元编程能力和丰富的内置API。Groovy代码可以直接编译为Java字节码,无缝运行在JVM上,并且能与Java类库完美互操作。
权威来源:Groovy官方文档
在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支持动态类型和静态类型,变量定义可省略类型声明(使用def关键字),也可显式指定类型(兼容Java)。
/**
* 变量定义示例
* @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"
Groovy的变量作用域与Java类似,分为局部变量、成员变量、静态变量:
/**
* 变量作用域示例
* @author ken
*/
class VariableScopeDemo {
// 成员变量(实例变量)
String memberVar = "成员变量"
// 静态变量(类变量)
static String staticVar = "静态变量"
void testScope() {
// 局部变量
def localVar = "局部变量"
println("局部变量:${localVar}")
println("成员变量:${memberVar}")
println("静态变量:${staticVar}")
}
}
// 运行测试
new VariableScopeDemo().testScope()
// 输出结果:
// 局部变量:局部变量
// 成员变量:成员变量
// 静态变量:静态变量
Groovy的核心数据类型与Java一致(基本类型+包装类型),同时扩展了部分实用类型(如GString、Range等)。
/**
* 基本数据类型示例
* @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
${}包裹,比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
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
Groovy支持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
/**
* 安全导航运算符示例
* @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
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) // 李四
/**
* 安全类型转换运算符示例
* @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]
Groovy的流程控制语句与Java基本一致,包括if-else、for、while、switch等,但语法更简洁。
/**
* if-else示例
* @author ken
*/
def score = 85
if (score >= 90) {
println("优秀")
} else if (score >= 80) {
println("良好")
} else if (score >= 60) {
println("及格")
} else {
println("不及格")
}
// 输出:良好
/**
* 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}")
}
/**
* 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)
Groovy的switch支持整数、字符串、表达式、类型判断等,比Java更强大。
/**
* 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集合进行了深度扩展,提供了大量便捷的方法,让集合操作更简洁高效。
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
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}")
Groovy的Set默认是java.util.LinkedHashSet,定义语法为[元素1, 元素2, ...] as Set或new 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) }
Groovy的方法定义比Java更简洁,支持可选参数、默认参数、变长参数,且方法调用时可省略括号(特殊场景)。
/**
* 基础方法定义示例
* @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
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, 王五~
与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
当方法调用是语句的最后一个表达式,且参数明确时,可省略括号(增强可读性)。
/**
* 方法调用省略括号示例
* @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)
Groovy的类定义兼容Java,同时提供了大量语法糖,如简化的构造器、属性访问器、静态导入等。
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
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}
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
闭包是Groovy的核心特性之一,它是一段可执行的代码块,能够捕获上下文的变量和方法。闭包可作为参数传递、赋值给变量,极大增强了代码的灵活性。
闭包的定义语法为{ [参数列表] -> 代码块 },参数列表可选,若省略参数列表,默认有一个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
闭包最常用的场景是作为方法参数,实现回调功能(类似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}")
}
闭包能够捕获定义它的上下文环境中的变量和方法,即使在上下文之外执行也能访问。
/**
* 闭包捕获上下文变量示例
* @author ken
*/
def outerVar = "外部变量"
// 定义闭包,捕获外部变量
def closure = {
println("捕获外部变量:${outerVar}")
}
// 在上下文之外调用闭包
closure() // 输出:捕获外部变量:外部变量
// 修改外部变量,闭包中也会同步变化
outerVar = "修改后的外部变量"
closure() // 输出:捕获外部变量:修改后的外部变量
// 闭包中修改外部变量
def count = 0
def incrementClosure = {
count++
}
incrementClosure()
incrementClosure()
println("count:${count}") // 输出:2
Groovy闭包有三个重要的属性:this、owner、delegate,用于指定闭包执行时的上下文。
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基于JVM实现了强大的元编程能力,允许在运行时动态修改类的行为、添加方法、拦截方法调用等。核心机制包括:Expando、方法拦截、元类(MetaClass)。
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
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, 李四(拦截后)
Groovy的异常处理与Java基本一致,支持try-catch-finally、抛出异常,但语法更简洁,且支持多异常捕获。
/**
* 异常处理示例
* @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("多异常处理结束")
}
Groovy提供了简洁的文件操作API,相比Java的IO操作更高效,无需手动关闭流(自动资源管理)。
/**
* 文件读取示例
* @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)
}
}
/**
* 文件写入示例
* @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) }
}
Groovy可无缝集成MyBatis-Plus,实现数据库操作。以下是完整的实战示例(基于MySQL 8.0)。
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
/**
* 用户实体类
* @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
}
/**
* 用户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> {
}
/**
* 用户服务类
* @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)
}
}
/**
* 用户控制器
* @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)
}
}
/**
* 启动类
* @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)
}
}
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 | 核心差异说明与实战影响 |
|---|---|---|---|
变量定义 | 支持 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 无此功能 |
Groovy 作为动态语言,核心设计目标是 “简化开发、提升效率”,通过动态类型推断、语法糖、原生特性(闭包、元编程)减少模板代码;而 Java 作为静态语言,核心目标是 “类型安全、性能稳定”,通过严格的类型检查、编译期校验保障大型项目的可维护性。
两者底层均基于 JVM,字节码层面可无缝交互,因此实际开发中常采用 “Groovy 负责快速开发脚本/工具类,Java 负责核心业务逻辑” 的混合模式,兼顾效率与稳定性。
以下示例展示 Groovy 与 Java 混合开发的核心场景:Groovy 编写工具类(简化文件操作、集合处理),Java 编写核心业务服务,两者无缝调用。
/**
* 文件操作工具类(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)
}
}
}
/**
* 用户业务服务(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;
}
}
/**
* 混合开发测试类
* @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 }
}
}
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
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")
}
}
利用 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("用户数据迁移脚本执行完成")
}
}
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'
}
}
}
利用 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()) 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 动态类型带来便利的同时,也可能导致性能损耗。以下是针对核心场景的优化方案,平衡开发效率与性能:
静态类型优化:
示例:
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
}
}
@CompileStatic,将 Groovy 代码编译为静态类型字节码,性能接近 Java集合操作优化:
Lists.newArrayList() 预分配容量each 闭包(内部优化了迭代器),避免使用 for in 循环(动态类型迭代开销较大)元编程性能优化:
Expando 而非动态修改已有类的元类,减少全局影响文件操作优化:
eachLine 按行读取,避免 text 方法一次性加载全部内容到内存withWriter 结合缓冲区,减少 IO 次数@CompileStatic)ExceptionGroovy 作为基于 JVM 的动态脚本语言,核心优势在于 “简化开发、提升效率”,通过兼容 Java 语法、提供丰富语法糖(字符串插值、简化集合操作、闭包)、强大的元编程能力,成为脚本开发、工具类编写、DevOps 自动化的优选语言。
与 Java 混合开发时,可充分发挥两者优势:Java 保障核心业务的类型安全与稳定性,Groovy 提升辅助功能的开发效率。掌握 Groovy 的关键在于理解其动态特性与 Java 静态特性的差异,结合实战场景灵活选型。
通过本文的系统讲解与实战示例,相信开发者已能掌握 Groovy 核心语法与应用场景。建议结合实际项目需求,从脚本编写、工具类开发入手,逐步深入元编程、DSL 等高级特性,充分发挥 Groovy 提升开发效率的优势。