Перейти к основному содержимому

5.12. Справочник по Groovy

Разработчику Архитектору

Справочник по Groovy

1. Типы данных

Простые типы

  • null — отсутствие значения.
  • boolean — логическое значение: true, false.
  • char — одиночный символ в одинарных кавычках: 'A'.
  • int, long, BigInteger — целочисленные типы.
  • float, double, BigDecimal — числа с плавающей точкой.

Строки

  • GString ("...") — интерполируемая строка, поддерживает выражения ${выражение}.
  • String ('...') — обычная строка без интерполяции.
  • Triple-quoted string ('''...''') — многострочная строка без интерполяции.
  • Slashy string (/.../) — удобна для регулярных выражений, не требует экранирования слешей.
  • Dollar-slashy string ($/.../$) — многострочная строка с интерполяцией и минимальным экранированием.

Коллекции

  • List — упорядоченный список, изменяемый по умолчанию:
    def list = [1, 2, 3]
  • Set — множество уникальных элементов:
    def set = [1, 2, 3] as Set
  • Map — ассоциативный массив:
    def map = [name: 'Alice', age: 30]

Диапазоны (Ranges)

  • Используются для перебора значений:
    def range = 1..5          // включительно
    def exclusive = 1..<5 // исключительно

Файлы и ресурсы

  • Объект File используется напрямую из Java.
  • Groovy добавляет методы вроде eachLine, splitEachLine, withReader.

2. Операторы

Арифметические

  • +, -, *, /, %, ** (возведение в степень)

Логические

  • &&, ||, !

Побитовые

  • &, |, ^, ~, <<, >>, >>>

Операторы сравнения

  • ==, !=, <, <=, >, >=
  • Groovy переопределяет == как вызов .equals(), а не сравнение ссылок.

Операторы присваивания

  • =, +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<=, >>=, >>>=

Операторы безопасной навигации

  • ?. — вызов метода или доступ к свойству только если объект не null:
    person?.address?.city

Оператор Elvis

  • ?: — возвращает левый операнд, если он не null и не false; иначе — правый:
    name = providedName ?: 'Anonymous'

Оператор распространения (Spread operator)

  • *. — применяет метод ко всем элементам коллекции:
    names*.toUpperCase()

Оператор объединения списков

  • + — объединяет списки или карты.
  • - — удаляет элементы из списка или ключи из карты.

Оператор индексации

  • [] — доступ к элементу по индексу или ключу:
    list[0], map['key'], map.key

Оператор диапазона

  • .., ..<

Оператор замыкания

  • { параметры -> тело }

3. Управляющие конструкции

Условные операторы

  • if / else if / else
  • switch / case — поддерживает сопоставление с любыми типами, включая классы, регулярные выражения, замыкания.

Циклы

  • for (i in collection) — итерация по коллекции, диапазону, массиву.
  • while (condition) { ... }
  • loop.each { item -> ... } — функциональный стиль через замыкания.

Обработка исключений

  • try / catch / finally — стандартный механизм Java, полностью поддерживается.

4. Методы и функции

Объявление

  • Методы объявляются с помощью ключевого слова def или явного типа возврата:
    def greet(name) { "Hello, $name!" }
    String greet(String name) { "Hello, $name!" }

Необязательные параметры

  • Поддерживаются значения по умолчанию:
    def log(message, level = 'INFO') { ... }

Переменное число аргументов

  • Используется оператор ...:
    def sum(int... numbers) { numbers.sum() }

Явный возврат

  • Последнее выражение в методе автоматически возвращается, но можно использовать return.

5. Классы и объекты

Объявление класса

class Person {
String name
int age

String toString() { "$name ($age)" }
}

Автоматические геттеры и сеттеры

  • Все поля получают геттеры и сеттеры по соглашению.

Конструкторы

  • Конструктор по умолчанию принимает Map:
    def p = new Person(name: 'Bob', age: 25)

Наследование

  • Поддерживается через extends.

Интерфейсы и трейты

  • Интерфейсы работают как в Java.
  • Traits — мощный механизм повторного использования кода:
    trait Flyable { void fly() { println "Flying!" } }
    class Bird implements Flyable { }

Аннотации

  • Поддерживаются все Java-аннотации.
  • Groovy предоставляет собственные аннотации: @CompileStatic, @TypeChecked, @Delegate, @Lazy, @Immutable, @Sortable, @Canonical, и другие.

6. Замыкания (Closures)

Синтаксис

{ param1, param2 -> body }

Неявные параметры

  • it — имя по умолчанию для единственного параметра:
    [1, 2, 3].each { println it }

Делегирование

  • Замыкание может иметь delegate, который определяет контекст выполнения:
    closure.delegate = someObject
    closure.resolveStrategy = Closure.DELEGATE_FIRST

Полезные методы для замыканий

  • call(), curry(), rcurry(), memoize(), trampoline()

7. Коллекции и их методы

List

  • size(), isEmpty(), get(index), putAt(index, value)
  • each, collect, findAll, any, every, groupBy, sort, reverse, unique, flatten, sum, min, max

Map

  • keySet(), values(), entrySet()
  • each, collectEntries, findAll, any, every, groupBy, subMap

String

  • eachMatch(regex), tokenize(), padLeft(), padRight(), toInteger(), toDouble(), replaceAll(), eachLine()

8. Метапрограммирование

Runtime metaprogramming

  • Добавление методов и свойств во время выполнения:
    Person.metaClass.greet = { -> "Hi from $name" }

ExpandoMetaClass

  • Включается глобально или для конкретного класса.

methodMissing / propertyMissing

  • Перехват вызовов несуществующих методов или свойств.

invokeMethod

  • Перехват всех вызовов методов объекта.

Categories

  • Временное расширение поведения классов в блоке use.

AST Transformations (compile-time)

  • Изменяют дерево абстрактного синтаксиса на этапе компиляции.
  • Примеры: @ToString, @EqualsAndHashCode, @TupleConstructor, @Bindable, @ListenerList

9. Компиляция и настройки

Режимы компиляции

  • Dynamic — поведение по умолчанию, проверка типов во время выполнения.
  • Static compilation — через @CompileStatic, обеспечивает производительность как у Java.
  • Type checking — через @TypeChecked, проверяет типы, но оставляет динамическое поведение.

Параметры компилятора Groovy

  • indy — использование invokedynamic (JDK 7+).
  • encoding — кодировка исходного файла.
  • targetBytecode — версия байткода (например, 1.8, 11, 17).
  • parameters — сохранение имён параметров в байткоде.
  • previewFeatures — включение preview-функций JDK.

Конфигурация через groovyOptions

В Gradle:

compileGroovy {
groovyOptions.optimizationOptions.indy = true
groovyOptions.configurationScript = file('config.groovy')
}

10. Интеграция с Java

  • Любой Java-класс доступен напрямую.
  • Groovy-классы компилируются в байткод JVM и могут использоваться из Java.
  • Совместимость с Java Collections API, Streams, Optional и другими конструкциями.

11. DSL и скриптование

GroovyShell

  • Выполнение строк как кода:
    new GroovyShell().evaluate('2 + 3')

GroovyScriptEngine

  • Загрузка и выполнение скриптов из файлов.

Builder-паттерн

  • Встроенные MarkupBuilder, JsonBuilder, StreamingJsonBuilder, ObjectGraphBuilder.

Пример:

def builder = new MarkupBuilder()
builder.person {
name 'Alice'
age 30
}

12. Распространённые утилиты и расширения

Расширения для чисел

  • times { ... } — повторить действие N раз.
  • upto(to) { ... }, downto(to) { ... }

Расширения для файлов

  • eachFile, eachDir, deleteDir(), getText(), write(text)

Расширения для потоков

  • withStream { ... }, withWriter { ... }

Расширения для дат

  • Date.plus(days), Date.minus(days), parse(format, string)

13. Безопасность и ограничения

  • При выполнении внешних скриптов используйте SecureASTCustomizer для ограничения доступа к опасным API.
  • Отключите возможность создания новых классов, вызова System.exit, доступа к файловой системе и т.п.

Пример:

def secure = new SecureASTCustomizer()
secure.closuresAllowed = false
secure.methodDefinitionAllowed = false

14. AST-трансформации (Compile-time Metaprogramming)

Groovy позволяет изменять структуру кода на этапе компиляции через аннотации, которые преобразуют абстрактное синтаксическое дерево (AST). Это мощный механизм для генерации кода без рантайм-накладных расходов.

Встроенные AST-трансформации

@Canonical

Объединяет @ToString, @EqualsAndHashCode, @TupleConstructor:

@Canonical
class Person {
String name
int age
}

@ToString

Генерирует метод toString():

@ToString(includeNames = true, includePackage = false)
class Point { int x, y }
// Результат: Point(x:10, y:20)

Параметры:

  • includeNames — добавлять имена полей.
  • includePackage — включать имя пакета.
  • excludes — исключить поля.
  • includes — включить только указанные поля.

@EqualsAndHashCode

Генерирует equals() и hashCode().

@TupleConstructor

Создаёт конструктор на основе полей:

@TupleConstructor
class Point { int x, y }
def p = new Point(1, 2)

@Immutable

Делает класс неизменяемым:

  • Все поля становятся final.
  • Генерируются геттеры.
  • Коллекции оборачиваются в неизменяемые обёртки.
  • Класс объявляется final.

@Sortable

Добавляет реализацию Comparable и статические методы comparatorBy*.

@Delegate

Делегирует вызовы методов другому объекту:

class Engine {
void start() { println "Engine started" }
}

class Car {
@Delegate Engine engine = new Engine()
}
new Car().start() // вызывает engine.start()

@Lazy

Ленивая инициализация поля:

class Heavy {
@Lazy def resource = { /* expensive init */ }()
}

@Synchronized

Автоматически оборачивает метод в synchronized.

@Field

Превращает переменную в поле класса (в скриптах):

@Field String name = 'Alice'

@PackageScope

Делает член класса доступным только в пределах пакета.

@Bindable и @Vetoable

Используются в GUI-приложениях для поддержки property change listeners.


15. Расширенное метапрограммирование

ExpandoMetaClass

Позволяет динамически добавлять методы, свойства, статические методы:

String.metaClass.shout = { -> delegate.toUpperCase() + '!' }
println "hello".shout() // HELLO!

Включение глобально:

ExpandoMetaClass.enableGlobally()

methodMissing и propertyMissing

Перехват несуществующих вызовов:

class Dynamic {
def methodMissing(String name, args) {
return "Called $name with ${args.join(', ')}"
}
}

invokeMethod

Перехват всех вызовов методов (включая существующие, если metaClass.invokeMethod переопределён).

getProperty / setProperty

Контроль доступа к свойствам.

Categories

Временное расширение поведения:

class NumberUtils {
static double square(Number n) { n * n }
}

use(NumberUtils) {
println 5.square() // 25.0
}

16. Работа с данными

JSON

Чтение

def json = new JsonSlurper().parseText('{"name":"Alice"}')
println json.name

Запись

def builder = new JsonBuilder()
builder.person {
name 'Alice'
age 30
}
println builder.toPrettyString()

StreamingJsonBuilder

Для больших объёмов данных:

def writer = new StringWriter()
def builder = new StreamingJsonBuilder(writer)
builder.people {
person { name 'Alice' }
}

XML

Parsing

def xml = '''
<books>
<book title="Groovy Guide"/>
</books>
'''
def data = new XmlSlurper().parseText(xml)
println data.book[0].@title

Построение

def builder = new MarkupBuilder()
builder.books {
book(title: 'Groovy Guide')
}

XPath-подобные запросы

data.book.findAll { it.@year.toInteger() > 2020 }

17. Шаблоны

SimpleTemplateEngine

Поддерживает ${выражения}:

def engine = new SimpleTemplateEngine()
def template = engine.createTemplate('Hello, $name!')
def result = template.make(name: 'Alice')

GStringTemplateEngine

Более мощный, поддерживает логику внутри шаблона.

XmlTemplateEngine

Для генерации XML с логикой.


18. Интеграция с Gradle

Groovy — основной язык написания скриптов сборки в Gradle (до появления Kotlin DSL).

Пример build.gradle:

plugins {
id 'groovy'
}

repositories {
mavenCentral()
}

dependencies {
implementation 'org.codehaus.groovy:groovy-all:4.0.15'
testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
}

Кастомные задачи на Groovy

task hello {
doLast {
println "Hello from Groovy in Gradle!"
}
}

Конфигурация компилятора

compileGroovy {
groovyOptions.optimizationOptions.indy = true
groovyOptions.targetBytecode = '17'
}

19. Тестирование: Spock Framework

Spock — BDD-фреймворк на Groovy, сочетающий выразительность и мощь.

Структура спецификации

class MathSpec extends Specification {
def "sum of two numbers"() {
expect:
a + b == c

where:
a | b || c
1 | 2 || 3
4 | 5 || 9
}
}

Блоки

  • given — подготовка
  • when — действие
  • then — проверка
  • expect — комбинация when/then
  • where — параметризация
  • cleanup — завершение
  • setup — аналог @Before

Моки и спайки

def service = Mock(Service)
service.process(_) >> "OK"

20. Производительность и оптимизация

Статическая компиляция

Используйте @CompileStatic для критичных к производительности участков:

@CompileStatic
int factorial(int n) {
n <= 1 ? 1 : n * factorial(n - 1)
}

Избегайте динамических вызовов в циклах

Кэшируйте методы или используйте интерфейсы.

Используйте @TypeChecked для поиска ошибок

Проверяет типы, но сохраняет динамические возможности при необходимости.

invokedynamic (indy)

Включите флаг -indy при компиляции для ускорения вызовов замыканий и динамических методов (требует JDK 7+).

Профилирование

Используйте VisualVM, JProfiler или async-profiler для анализа узких мест.


21. Практическое применение Groovy

Скрипты автоматизации

  • Обработка логов
  • Генерация конфигураций
  • Миграции данных

DSL для конфигурации

server {
port 8080
host 'localhost'
ssl enabled: true
}

Jenkins Pipelines

Declarative и Scripted Pipelines на Groovy.

Тестовые фреймворки

  • Spock
  • Geb (для UI-тестов)

Встраивание в Java-приложения

  • Через GroovyShell
  • Через GroovyScriptEngine для горячей замены логики

22. Версии и совместимость

  • Groovy 2.x — поддержка JDK 6–8, начало AST-трансформаций.
  • Groovy 3.x — поддержка Java 8+, новые операторы (!in, !instanceof), улучшенная совместимость с Java.
  • Groovy 4.x — поддержка JDK 17, улучшенная статическая компиляция, модульность, Jakarta EE.

Рекомендуется использовать Groovy 4.0+ для новых проектов.


23. Ограничения и предостережения

  • Динамическая природа снижает производительность по сравнению с Java.
  • Отладка динамического кода сложнее.
  • Некоторые IDE хуже поддерживают Groovy, чем Java.
  • При смешивании с Java возможны неочевидные ошибки из-за различий в семантике ==, in, [].

24. Встроенные методы расширений (GDK — Groovy Development Kit)

Groovy автоматически добавляет сотни полезных методов к стандартным классам Java через механизм метаклассов. Эти методы определены в классах:

  • DefaultGroovyMethods — для большинства объектов
  • DefaultGroovyStaticMethods — статические методы
  • StringGroovyMethods, FileGroovyMethods, IOGroovyMethods, SqlGroovyMethods и др.

Эти методы доступны везде, без импорта.

Общие методы для коллекций

each

[1, 2, 3].each { println it }

collect

Преобразует каждый элемент:

[1, 2, 3].collect { it * 2 } // [2, 4, 6]

findAll / grep

Фильтрация:

[1, 2, 3, 4].findAll { it % 2 == 0 } // [2, 4]
['a1', 'b2', 'c3'].grep(~/\d$/) // строки, оканчивающиеся цифрой

any / every

Проверка условий:

[1, 2, 3].any { it > 2 }   // true
[1, 2, 3].every { it > 0 } // true

groupBy

Группировка:

['apple', 'banana', 'cherry'].groupBy { it[0] }
// [a:['apple'], b:['banana'], c:['cherry']]

sum, min, max

[1, 2, 3].sum() // 6
[1, 2, 3].max() // 3

inject (аналог reduce)

[1, 2, 3].inject(0) { acc, val -> acc + val } // 6

flatten

Разворачивает вложенные коллекции:

[[1, 2], [3, 4]].flatten() // [1, 2, 3, 4]

unique

Удаляет дубликаты:

[1, 2, 2, 3].unique() // [1, 2, 3]

sort

Сортировка:

[3, 1, 2].sort() // [1, 2, 3]
people.sort { it.age }

reverse

Инверсия порядка.

pop, push, shift, unshift

Для работы как со стеком или очередью:

def stack = [1, 2, 3]
stack.push(4) // [1, 2, 3, 4]
stack.pop() // 4

Примечание: push/pop работают с концом списка, shift/unshift — с началом.


Методы для строк

toInteger(), toDouble(), toBoolean()

Безопасное преобразование:

'123'.toInteger() // 123
'false'.toBoolean() // false

padLeft(), padRight()

'5'.padLeft(3, '0') // '005'

tokenize()

Разделение без регулярных выражений:

'a,b,c'.tokenize(',') // ['a', 'b', 'c']

eachMatch(regex)

Итерация по совпадениям:

'abc123def456'.eachMatch(/\d+/) { match -> println match[0] }
// 123
// 456

contains(), startsWith(), endsWith() — уже есть в Java, но Groovy делает их null-safe.


Методы для файлов и потоков

eachLine

new File('data.txt').eachLine { line -> println line }

splitEachLine

file.splitEachLine(/\t/) { parts -> println parts[0] }

withReader, withWriter, withInputStream, withOutputStream

Автоматическое закрытие ресурсов:

file.withReader { reader -> ... }

getText(), write(text), append(text)

def content = file.getText('UTF-8')
file.write('Hello', 'UTF-8')

eachFile, eachDir, traverse

Рекурсивный обход:

dir.traverse { file -> if (file.name.endsWith('.groovy')) println file }

deleteDir()

Рекурсивное удаление каталога.


Методы для чисел

times

5.times { println 'Hello' }

upto, downto

1.upto(3) { println it } // 1, 2, 3
5.downto(3) { println it } // 5, 4, 3

step

0.step(10, 2) { println it } // 0, 2, 4, 6, 8

Методы для карт (Map)

get(key, defaultValue)

map.get('name', 'Unknown')

subMap(keys)

map.subMap(['name', 'age'])

collectEntries

Преобразование в новую карту:

[1, 2, 3].collectEntries { [it, it * it] } // [1:1, 2:4, 3:9]

each, eachWithIndex

map.each { key, value -> println "$key = $value" }

25. Регулярные выражения

Groovy упрощает работу с регулярками.

Создание

  • Используйте slashy strings (/.../) — не нужно экранировать \:
    def pattern = /\d{3}-\d{2}-\d{4}/

Сопоставление

  • Оператор ==~ — полное совпадение:
    '123-45-6789' ==~ /\d{3}-\d{2}-\d{4}/ // true
  • Оператор =~ — частичное совпадение, возвращает java.util.regex.Matcher:
    def matcher = 'ID: 123' =~ /(\d+)/
    if (matcher) {
    println matcher[0][1] // '123'
    }

Замена

'Price: $100'.replaceFirst(/\$(\d+)/) { all, amount -> "€${amount}" }
// Price: €100

Группы

def m = 'John Doe' =~ /(\w+) (\w+)/
if (m) {
def (full, first, last) = m[0]
println "First: $first, Last: $last"
}

26. Многопоточность и параллелизм

GPars (Groovy Parallel Systems)

Хотя GPars больше не активно развивается, его идеи повлияли на современные подходы. В Groovy 4+ рекомендуется использовать Java concurrency utilities, но с Groovy-синтаксисом.

Пример с ExecutorService

def service = Executors.newFixedThreadPool(4)
def futures = (1..10).collect {
service.submit { it * it }
}
futures*.get().each { println it }
service.shutdown()

Параллельная обработка коллекций

def results = [1, 2, 3, 4].parallelStream().map { it * 2 }.toList()

Groovy не добавляет собственных примитивов для async/await, но легко интегрируется с CompletableFuture:

def future = CompletableFuture.supplyAsync { 42 }
future.thenAccept { println it }.join()

27. Управление ресурсами и замыкания

Groovy поощряет использование замыканий для управления жизненным циклом:

new File('input.txt').withReader { reader ->
new File('output.txt').withWriter { writer ->
reader.eachLine { line ->
writer.println line.toUpperCase()
}
}
}

Аналогично для:

  • Баз данных (Sql.withInstance)
  • HTTP-соединений
  • ZIP-архивов
  • Сокетов

Это гарантирует вызов close(), даже при исключении.


28. Практические паттерны использования

1. Конфигурационные файлы

config.groovy:

server {
port = 8080
ssl = true
hosts = ['localhost', '127.0.0.1']
}

Загрузка:

def config = new ConfigSlurper().parse(new File('config.groovy').toURL())
println config.server.port

2. ETL-скрипты

new File('input.csv').splitEachLine(',') { fields ->
def record = [id: fields[0], name: fields[1]]
if (record.name) db.insert(record)
}

3. Генерация кода

def template = '''
class ${className} {
${fields.collect { "String $it" }.join('\n ')}
}
'''
def engine = new SimpleTemplateEngine()
println engine.createTemplate(template).make(className: 'User', fields: ['name', 'email'])

4. Административные скрипты

  • Очистка логов
  • Архивирование
  • Мониторинг

5. Jenkins Shared Libraries

DSL на Groovy для стандартизации CI/CD.


29. Инструменты и экосистема

ИнструментНазначение
Groovy ConsoleИнтерактивная среда выполнения
GroovyShellREPL для экспериментов
GradleСборка и управление зависимостями
SpockТестирование
GebUI-тестирование (Selenium + jQuery-подобный синтаксис)
RatpackЛёгкий фреймворк для веб-API
GrailsFull-stack веб-фреймворк (на базе Spring Boot)

30. Советы по стилю и читаемости

  • Используйте def только когда тип не важен или выводится.
  • Предпочитайте @CompileStatic в производственном коде.
  • Избегайте глобального метапрограммирования в больших проектах.
  • Используйте ConfigSlurper вместо Properties.
  • Пишите DSL с Closure.DELEGATE_FIRST.
  • Для скриптов — минимум зависимостей, максимум автономности.

31. Структура типичного Groovy-проекта

Groovy не навязывает жёсткой структуры, но следует общепринятым соглашениям JVM-экосистемы, особенно если используется Gradle или Maven.

Минимальная структура (скриптовый проект)

my-groovy-project/
├── scripts/
│ ├── deploy.groovy
│ └── backup.groovy
├── lib/
│ └── helpers.groovy
├── config/
│ └── app.groovy
└── README.md

Стандартная структура (Gradle-проект)

src/
├── main/
│ ├── groovy/ # основной код
│ └── resources/ # конфиги, шаблоны, данные
└── test/
├── groovy/ # тесты (Spock, JUnit)
└── resources/ # тестовые ресурсы
build.gradle
gradle.properties
settings.gradle

Модульный проект (несколько подпроектов)

project-root/
├── core/ # ядро: DSL, утилиты
├── cli/ # командная оболочка
├── web/ # веб-интерфейс (Ratpack/Grails)
├── scripts/ # автономные скрипты
└── build.gradle # корневой билд-файл

Важно: Groovy-файлы могут содержать как классы, так и свободные выражения (скрипты). Файл с class компилируется в .class, файл без — в наследника groovy.lang.Script.


32. Организация кода

Классы vs Скрипты

  • Классы — для переиспользуемой логики, библиотек, компонентов.
  • Скрипты — для автоматизации, одноразовых задач, точек входа.

Пример скрипта (hello.groovy):

println "Hello from script!"
def name = args[0] ?: 'World'
println "Hi, $name!"

Запуск:

groovy hello.groovy Alice

Пакеты и импорты

  • Используются так же, как в Java.
  • Groovy позволяет опускать package в скриптах, но рекомендуется указывать в библиотеках.

Разделение ответственности

  • DSL-слои — декларативное описание.
  • Интерпретаторы — преобразование DSL в действия.
  • Инфраструктурные утилиты — работа с файлами, сетью, БД.

33. Управление зависимостями

Через Gradle

dependencies {
implementation 'org.codehaus.groovy:groovy-all:4.0.15'
implementation 'org.apache.commons:commons-text:1.10.0'
testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
}

Через @Grab (в скриптах)

@Grab('org.apache.commons:commons-text:1.10.0')
import org.apache.commons.text.WordUtils

println WordUtils.capitalize('hello world')

Предупреждение: @Grab использует Ivy под капотом, может быть медленным и ненадёжным в изолированных средах. В продакшене предпочтительнее Gradle/Maven.


34. Конфигурация приложения

ConfigSlurper

Читает .groovy-файлы как конфигурацию:

// config/app.groovy
environments {
development {
db.url = 'jdbc:h2:mem:dev'
}
production {
db.url = 'jdbc:postgresql://prod/db'
}
}

Загрузка:

def env = System.getenv('ENV') ?: 'development'
def config = new ConfigSlurper(env).parse(new File('config/app.groovy').toURL())
println config.db.url

Поддержка иерархии

  • Возможны вложенные блоки.
  • Поддержка include через ConfigObject.merge().

Альтернативы

  • JSON/YAML через JsonSlurper / YamlSlurper (из SnakeYAML).
  • Properties — через Properties.

35. Настройка компилятора Groovy

Параметры компиляции (через CLI)

groovyc -j -indy -targetBytecode 17 src/*.groovy

Флаги:

  • -j — совместная компиляция Java + Groovy
  • -indy — использовать invokedynamic
  • -targetBytecode — версия JVM
  • -encoding UTF-8

Через Gradle

compileGroovy {
groovyOptions.optimizationOptions.indy = true
groovyOptions.encoding = 'UTF-8'
groovyOptions.targetBytecode = '17'
}

Custom Compilation Configuration

Можно создать groovyCompiler.groovy:

withConfig(configuration) {
ast(groovy.transform.CompileStatic)
}

И подключить:

compileGroovy {
groovyOptions.configurationScript = file('groovyCompiler.groovy')
}

36. Обработка ошибок и логирование

Исключения

  • Используются стандартные Java-механизмы.
  • Groovy не требует объявления throws.

Логирование

Рекомендуется SLF4J:

@Grab('org.slf4j:slf4j-simple:2.0.7')
import org.slf4j.LoggerFactory

def log = LoggerFactory.getLogger(this.class)
log.info("Processing item")

Или встроенное логирование через java.util.logging:

def logger = java.util.logging.Logger.getLogger('MyApp')
logger.info('Started')

37. Тестирование и проверка качества

Spock — основной выбор

  • Человекочитаемые спецификации.
  • Встроенные моки.
  • Параметризованные тесты.

Покрытие кода

  • JaCoCo через Gradle:
    plugins { id 'jacoco' }
    jacocoTestReport {
    reports {
    html.required = true
    xml.required = false
    }
    }

Статический анализ

  • CodeNarc — линтер для Groovy:
    plugins { id 'codenarc' }
    codenarcMain {
    configFile = file('config/codenarc.groovy')
    }

Пример codenarc.groovy:

ruleset {
ruleset('rulesets/basic.xml')
ruleset('rulesets/braces.xml')
ruleset('rulesets/naming.xml')
}

38. Запуск и развёртывание

Самодостаточный JAR (fat jar)

jar {
manifest {
attributes 'Main-Class': 'MyApp'
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
}

Скрипты как исполняемые файлы (Unix)

#!/usr/bin/env groovy
println "Running as script"

Убедитесь, что groovy в PATH.

Docker-образ

FROM openjdk:17-jdk-slim
COPY build/libs/app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]

39. Безопасность при выполнении скриптов

Если вы выполняете внешние Groovy-скрипты (например, от пользователей), обязательно ограничьте окружение:

SecureASTCustomizer

def secure = new SecureASTCustomizer()
secure.closuresAllowed = false
secure.methodDefinitionAllowed = false
secure.importsWhitelist = ['java.lang.String']
secure.starImportsWhitelist = []
secure.staticImportsWhitelist = []
secure.tokensBlacklist = [TokenConstants.ASSIGN, TokenConstants.PLUS_PLUS]

def compiler = new CompilerConfiguration()
compiler.addCompilationCustomizers(secure)

def shell = new GroovyShell(this.class.classLoader, new Binding(), compiler)
shell.evaluate(untrustedCode)

Ограничение доступа к файловой системе, сети, рефлексии.


40. Профилирование и отладка

Отладка в IDE

  • IntelliJ IDEA и Eclipse имеют отличную поддержку Groovy.
  • Точки останова работают в динамическом и статическом режимах.

Логирование состояния

  • Используйте dump() для быстрого инспектирования объекта:
    println person.dump()

Мониторинг производительности

  • VisualVM показывает вызовы Groovy-методов как обычные JVM-методы.
  • При использовании indy — убедитесь, что JIT работает эффективно.

41. Обновление и миграция

Groovy 2 → 3 → 4

Основные изменения:

  • Groovy 3: новые операторы (!in, !instanceof), поддержка Java 13+ синтаксиса.
  • Groovy 4: Jakarta EE вместо javax, улучшенная поддержка модулей, JDK 17+.

Проверка совместимости

  • Запустите тесты с новой версией.
  • Используйте @CompileStatic для выявления скрытых ошибок.

42. Когда использовать Groovy — и когда нет

Использовать Groovy, если:

  • Нужен выразительный DSL.
  • Требуется быстрая автоматизация.
  • Интеграция с существующим Java-кодом.
  • Пишутся тесты (Spock).
  • Создаётся конфигурационная система.

Рассмотреть альтернативы (Kotlin, Java), если:

  • Производительность критична на каждом этапе.
  • Команда не знакома с динамическими языками.
  • Требуется строгая типизация на этапе компиляции.
  • Проект масштабируется до сотен тысяч строк.