Статьи

Начало работы с Gradle: управление зависимостями

Сложно, если не невозможно, создавать реальные приложения, которые не имеют внешних зависимостей. Вот почему управление зависимостями является жизненно важной частью каждого программного проекта.

В этом блоге рассказывается, как мы можем управлять зависимостями наших проектов с помощью Gradle. Мы научимся настраивать используемые репозитории и необходимые зависимости. Мы также применим эту теорию к практике, реализовав простой пример приложения.

Давайте начнем.

Дополнительное чтение:

Введение в управление хранилищем

Репозитории по сути являются контейнерами зависимостей, и каждый проект может использовать ноль или более репозиториев.

Gradle поддерживает следующие форматы репозитория:

Давайте выясним, как мы можем настроить каждый тип репозитория в нашей сборке.

Добавление репозитория плюща в нашу сборку

Мы можем добавить репозиторий Ivy в нашу сборку, используя его URL-адрес или расположение в локальной файловой системе.

Если мы хотим добавить репозиторий Ivy, используя его URL-адрес, мы должны добавить следующий фрагмент кода в файл build.gradle :

1
2
3
4
5
repositories {
    ivy {
    }
}

Если мы хотим добавить репозиторий Ivy, используя его местоположение в файловой системе, нам нужно добавить следующий фрагмент кода в файл build.gradle :

1
2
3
4
5
repositories {
    ivy {      
        url "../ivy-repo"
    }
}

Если вы хотите получить больше информации о настройке репозиториев Ivy, вам следует проверить следующие ресурсы:

Давайте продолжим и узнаем, как мы можем добавить репозитории Maven в нашу сборку.

Добавление репозиториев Maven в нашу сборку

Мы можем добавить репозиторий Maven в нашу сборку, используя его URL-адрес или расположение в локальной файловой системе.

Если мы хотим добавить репозиторий Maven, используя его URL, мы должны добавить следующий фрагмент кода в файл build.gradle :

1
2
3
4
5
repositories {
    maven {
    }
}

Если мы хотим добавить репозиторий Maven, используя его местоположение в файловой системе, мы должны добавить следующий фрагмент кода в файл build.gradle :

1
2
3
4
5
repositories {
    maven {      
        url "../maven-repo"
    }
}

У Gradle есть три «псевдонима», которые мы можем использовать, когда добавляем репозитории Maven в нашу сборку. Эти псевдонимы:

Если мы хотим добавить центральный репозиторий Maven 2 в нашу сборку, мы должны добавить следующий фрагмент в наш файл build.gradle :

1
2
3
repositories {
    mavenCentral()
}

Если вы хотите получить больше информации о настройке репозиториев Maven, вам следует ознакомиться с разделом 50.6.4 Репозитории Maven Руководства пользователя Gradle .

Давайте продолжим и узнаем, как мы можем добавить репозитории плоских каталогов в нашу сборку.

Добавление хранилищ плоских каталогов в нашу сборку

Если мы хотим использовать репозитории плоских каталогов, мы должны добавить следующий фрагмент кода в наш файл build.gradle :

1
2
3
4
5
repositories {
    flatDir {
        dirs 'lib'
    }
}

Это означает, что зависимости ищутся из каталога lib . Также, если мы хотим, мы можем использовать несколько каталогов, добавив следующий фрагмент в файл build.gradle :

1
2
3
4
5
repositories {
    flatDir {
        dirs 'libA', 'libB'
    }
}

Если вы хотите получить больше информации о хранилищах плоских каталогов, вы должны проверить следующие ресурсы:

Давайте продолжим и выясним, как мы можем управлять зависимостями нашего проекта с помощью Gradle.

Введение в управление зависимостями

После того, как мы настроили репозитории нашего проекта, мы можем объявить его зависимости. Если мы хотим объявить новую зависимость, мы должны выполнить следующие шаги:

  1. Укажите конфигурацию зависимости.
  2. Объявите необходимую зависимость.

Давайте внимательнее посмотрим на эти шаги.

Группировка зависимостей в конфигурации

В Gradle зависимости группируются в именованный набор зависимостей. Эти группы называются конфигурациями, и мы используем их для объявления внешних зависимостей нашего проекта.

Плагин Java определяет несколько конфигураций зависимостей, которые описаны ниже:

  • Зависимости, добавленные в конфигурацию компиляции , требуются при компиляции нашего исходного кода нашего проекта.
  • Конфигурация времени выполнения содержит зависимости, которые требуются во время выполнения. Эта конфигурация содержит зависимости, добавленные в конфигурацию компиляции .
  • Конфигурация testCompile содержит зависимости, необходимые для компиляции тестов нашего проекта. Эта конфигурация содержит скомпилированные классы нашего проекта и зависимости, добавленные в конфигурацию компиляции .
  • Конфигурация testRuntime содержит зависимости, которые требуются при запуске наших тестов. Эта конфигурация содержит зависимости, добавленные в конфигурации compile , runtime и testCompile .
  • Конфигурация архива содержит артефакты (например, файлы Jar), ​​созданные нашим проектом.
  • Группа конфигурации по умолчанию содержит зависимости, которые требуются во время выполнения.

Давайте продолжим и выясним, как мы можем объявить зависимости нашего проекта Gradle.

Объявление зависимостей проекта

Наиболее распространенные зависимости называются внешними зависимостями, которые находятся во внешнем репозитории. Внешняя зависимость определяется с помощью следующих атрибутов:

  • Атрибут group идентифицирует группу зависимости (пользователи Maven знают этот атрибут как groupId ).
  • Атрибут name идентифицирует имя зависимости (пользователи Maven знают этот атрибут как artifactId ).
  • Атрибут version указывает версию внешней зависимости (пользователи Maven знают этот атрибут как версию ).

Эти атрибуты обязательны при использовании репозиториев Maven. Если вы используете другие репозитории, некоторые атрибуты могут быть необязательными.

Например, если вы используете хранилище плоских каталогов, вам может потребоваться указать только имя и версию .

Давайте предположим, что мы должны объявить следующую зависимость:

  • Группа зависимости — ‘foo’.
  • Название зависимости — ‘foo’.
  • Версия зависимости — 0.1.
  • Зависимость требуется при компиляции нашего проекта.

Мы можем объявить эту зависимость, добавив следующий код в файл build.gradle :

1
2
3
dependencies {
    compile group: 'foo', name: 'foo', version: '0.1'
}

Мы также можем объявить зависимости нашего проекта, используя ярлык, следующий синтаксису: [группа]: [имя]: [версия] . Если мы хотим использовать форму ярлыка, мы должны добавить следующий фрагмент кода в файл build.gradle :

1
2
3
dependencies {
    compile 'foo:foo:0.1'
}

Мы также можем добавить несколько зависимостей к одной и той же конфигурации. Если мы хотим использовать «нормальный» синтаксис при объявлении наших зависимостей, мы должны добавить следующий фрагмент кода в файл build.gradle :

1
2
3
4
5
6
dependencies {
    compile (
        [group: 'foo', name: 'foo', version: '0.1'],
        [group: 'bar', name: 'bar', version: '0.1']
    )
}

С другой стороны, если мы хотим использовать форму ярлыка, соответствующая часть файла build.gradle выглядит следующим образом:

1
2
3
dependencies {
    compile 'foo:foo:0.1', 'bar:bar:0.1'
}

Естественно, можно объявить зависимости, которые принадлежат разным конфигурациям. Например, если мы хотим объявить зависимости, которые принадлежат конфигурациям compile и testCompile , мы должны добавить следующий фрагмент кода в файл build.gradle :

1
2
3
4
dependencies {
    compile group: 'foo', name: 'foo', version: '0.1'
    testCompile group: 'test', name: 'test', version: '0.1'
}

Опять же, можно использовать ярлык формы. Если мы хотим объявить те же зависимости с помощью формы ярлыка, соответствующая часть файла build.gradle выглядит следующим образом:

1
2
3
4
dependencies {
    compile 'foo:foo:0.1'
    testCompile 'test:test:0.1'
}

Вы можете получить больше информации об объявлении ваших зависимостей, прочитав раздел 50.4 Как объявить ваши зависимости в Руководстве пользователя Gradle .

Теперь мы изучили основы управления зависимостями. Давайте перейдем к реализации нашего примера приложения.

Создание примера приложения

Требования нашего примера приложения описаны в следующем:

  • Сценарий сборки примера приложения должен использовать центральный репозиторий Maven.
  • Приложение-пример должно записать полученное сообщение в журнал, используя Log4j.
  • Пример приложения должен содержать модульные тесты, обеспечивающие возвращение правильного сообщения. Эти модульные тесты должны быть написаны с использованием JUnit.
  • Наш скрипт сборки должен создать исполняемый файл JAR.

Давайте выясним, как мы можем выполнить эти требования.

Конфигурирование репозиториев нашей сборки

Одним из требований нашего примера приложения было то, что его скрипт сборки должен использовать центральный репозиторий Maven. После того, как мы настроили наш скрипт сборки для использования центрального репозитория Maven, его исходный код выглядит следующим образом (соответствующая часть выделена):

01
02
03
04
05
06
07
08
09
10
11
apply plugin: 'java'
 
repositories {
    mavenCentral()
}
 
jar {
    manifest {
        attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
    }
}

Давайте двигаться дальше и объявим зависимости нашего примера приложения.

Объявление зависимостей нашего примера приложения

Мы должны объявить две зависимости в файле build.gradle :

  1. Log4j (версия 1.2.17) используется для записи полученного сообщения в журнал.
  2. JUnit (версия 4.11) используется для написания модульных тестов для нашего примера приложения.

После того, как мы объявили эти зависимости, файл build.gradle выглядит следующим образом (соответствующая часть выделена):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
apply plugin: 'java'
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile 'log4j:log4j:1.2.17'
    testCompile 'junit:junit:4.11'
}
 
jar {
    manifest {
        attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
    }
}

Давайте двигаться дальше и напишем некоторый код.

Написание кода

Чтобы выполнить требования нашего примера приложения, «мы должны его чрезмерно спроектировать». Мы можем создать пример приложения, выполнив следующие действия:

  1. Создайте класс MessageService, который возвращает строку «Hello World!» когда вызывается метод getMessage () .
  2. Создайте класс MessageServiceTest, который гарантирует, что метод getMessage () класса MessageService возвращает строку «Hello World!».
  3. Создайте основной класс нашего приложения, который получает сообщение от объекта MessageService и записывает сообщение в журнал, используя Log4j.
  4. Настройте Log4j.

Давайте пройдемся по этим шагам один за другим.

Сначала мы должны создать класс MessageService в каталоге src / main / java / net / petrikainulainen / gradle и реализовать его. После того, как мы это сделаем, его исходный код будет выглядеть следующим образом:

1
2
3
4
5
6
7
8
package net.petrikainulainen.gradle;
 
public class MessageService {
 
    public String getMessage() {
        return "Hello World!";
    }
}

Во-вторых , мы создали MessageServiceTest в каталоге src / main / test / net / petrikainulainen / gradle и записали модульный тест в метод getMessage () класса MessageService . Исходный код класса MessageServiceTest выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
package net.petrikainulainen.gradle;
 
import org.junit.Before;
import org.junit.Test;
 
import static org.junit.Assert.assertEquals;
 
public class MessageServiceTest {
 
    private MessageService messageService;
 
    @Before
    public void setUp() {
        messageService = new MessageService();
    }
 
    @Test
    public void getMessage_ShouldReturnMessage() {
        assertEquals("Hello World!", messageService.getMessage());
    }
}

В-третьих , мы создали класс HelloWorld в каталоге src / main / java / net / petrikainulainen / gradle . Этот класс является основным классом нашего приложения. Он получает сообщение от объекта MessageService и записывает его в журнал с помощью Log4j. Исходный код класса HelloWorld выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package net.petrikainulainen.gradle;
 
import org.apache.log4j.Logger;
 
public class HelloWorld {
 
    private static final Logger LOGGER = Logger.getLogger(HelloWorld.class);
 
    public static void main(String[] args) {
        MessageService messageService = new MessageService();
 
        String message = messageService.getMessage();
        LOGGER.info("Received message: " + message);
    }
}

В-четвертых , мы должны настроить Log4j с помощью log4j.properties, который находится в каталоге src / main / resources . Файл log4j.properties выглядит следующим образом:

1
2
3
4
5
log4j.appender.Stdout=org.apache.log4j.ConsoleAppender
log4j.appender.Stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.Stdout.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n
 
log4j.rootLogger=DEBUG,Stdout

Вот и все. Давайте выясним, как мы можем запустить тесты нашего примера приложения.

Запуск юнит-тестов

Мы можем запустить наш модульный тест с помощью следующей команды:

1
gradle test

Когда наш тест пройден, мы видим следующий вывод:

01
02
03
04
05
06
07
08
09
10
11
12
> gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
 
BUILD SUCCESSFUL
 
Total time: 4.678 secs

Однако, если наш модульный тест не пройден, мы увидим следующий результат (интересный раздел выделен):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
> gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
 
net.petrikainulainen.gradle.MessageServiceTest > getMessage_ShouldReturnMessageFAILED
    org.junit.ComparisonFailure at MessageServiceTest.java:22
 
1 test completed, 1 failed
:test FAILED
 
FAILURE: Build failed with an exception.
 
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/loke/Projects/Java/Blog/gradle-examples/dependency-management/build/reports/tests/index.html
 
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
 
BUILD FAILED
 
Total time: 4.461 secs

Как мы видим, если наши модульные тесты не пройдены, описываются:

  • какие тесты не удалось.
  • сколько тестов было выполнено и сколько тестов не удалось.
  • расположение отчета о тестировании, в котором содержится дополнительная информация о неудачных (и пройденных) тестах.

Когда мы запускаем наши модульные тесты, Gradle создает отчеты о тестировании в следующих каталогах:

  • Каталог build / test-results содержит необработанные данные каждого запуска теста.
  • Каталог build / reports / tests содержит HTML-отчет, который описывает результаты наших тестов.

Отчет о тестировании HTML является очень полезным инструментом, потому что он описывает причину, по которой наш тест не прошел Например, если наш модульный тест ожидает, что метод getMessage () класса MessageService возвращает строку «Hello Worl1d!», Отчет о тестировании HTML этого теста будет выглядеть следующим образом:

testfailure

Давайте продолжим и выясним, как мы можем упаковать и запустить наш пример приложения.

Упаковка и запуск нашего примера приложения

Мы можем упаковать наше приложение с помощью одной из следующих команд: em> сборка gradle или сборка gradle . Обе эти команды создают файл dependency-management.jar в каталоге build / libs .

Когда мы запустили наше приложение-пример с помощью команды java -jar dependency-management.jar , мы увидим следующий вывод:

01
02
03
04
05
06
07
08
09
10
11
12
13
> java -jar dependency-management.jar
  
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger
    at net.petrikainulainen.gradle.HelloWorld.<clinit>(HelloWorld.java:10)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 1 more

Причиной этого исключения является то, что зависимость Log4j не найдена в пути к классам при запуске нашего приложения.

Самый простой способ решить эту проблему — создать так называемый «толстый» файл jar. Это означает, что мы будем упаковывать необходимые зависимости в созданный файл JAR.

После того, как мы выполнили инструкции, приведенные в кулинарной книге Gradle , наш скрипт сборки выглядит следующим образом (соответствующая часть выделена):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
apply plugin: 'java'
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile 'log4j:log4j:1.2.17'
    testCompile 'junit:junit:4.11'
}
 
jar {
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    manifest {
        attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
    }
}

Теперь мы можем запустить пример приложения (после его упаковки) и, как видим, все работает правильно:

1
2
> java -jar dependency-management.jar
INFO  - HelloWorld                 - Received message: Hello World!

Это все на сегодня. Давайте подведем итог тому, что мы узнали из этого поста в блоге.

Резюме

Этот пост научил нас четырем вещам:

  • Мы узнали, как мы можем настроить репозитории, используемые нашей сборкой.
  • Мы узнали, как мы можем объявить требуемые зависимости и сгруппировать эти зависимости в конфигурации.
  • Мы узнали, что Gradle создает отчет о тестировании HTML при запуске наших тестов.
  • Мы узнали, как мы можем создать так называемый «толстый» файл jar.

Если вы хотите поиграть с примером приложения из этого блога, вы можете получить его от Github .