1. Обзор
Иногда во время разработки мы можем добавить больше зависимостей, чем используем.
В этом кратком руководстве мы увидим, как использовать плагин Gradle Nebula Lint для выявления и устранения подобных проблем.
2. Установка и конфигурация
В наших примерах мы используем многомодульную установку Gradle 5.
Этот плагин работает только с файлами сборки на основе Groovy.
Давайте настроим его в корневом файле сборки проекта:
plugins {
id "nebula.lint" version "16.9.0"
}
description = "Gradle 5 root project"
allprojects {
apply plugin :"java"
apply plugin :"nebula.lint"
gradleLint {
rules=['unused-dependency']
}
group = "com.foreach"
version = "0.0.1"
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
repositories {
jcenter()
}
}
На данный момент мы можем настроить его таким образом только для многопроектных сборок . Это означает, что мы не можем применять его отдельно в каждом модуле.
Далее давайте настроим зависимости нашего модуля:
description = "Gradle Unused Dependencies example"
dependencies {
implementation('com.google.guava:guava:29.0-jre')
testImplementation('junit:junit:4.12')
}
Теперь добавим простой основной класс в исходники нашего модуля:
public class UnusedDependencies {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
Мы будем опираться на это чуть позже и посмотрим, как работает плагин.
3. Сценарии обнаружения и отчеты
Плагин просматривает выходные файлы jar, чтобы определить, используется ли зависимость или нет.
Однако, в зависимости от нескольких условий , это может дать нам разные результаты .
Мы рассмотрим более интересные случаи в следующих разделах.
3.1. Неиспользуемые зависимости
Теперь, когда у нас есть настройки, давайте посмотрим на основной вариант использования. Нас интересуют неиспользуемые зависимости.
Запустим
задачу `` lintGradle :
$ ./gradlew lintGradle
> Task :lintGradle FAILED
# failure output omitted
warning unused-dependency this dependency is unused and can be removed
unused-dependencies/build.gradle:6
implementation('com.google.guava:guava:29.0-jre')
✖ 1 problem (0 errors, 1 warning)
To apply fixes automatically, run fixGradleLint, review, and commit the changes.
# some more failure output
Давайте посмотрим, что произошло. У нас есть неиспользуемая зависимость ( guava
) в нашей конфигурации compileClasspath
.
Если мы запустим задачу fixGradleLint
, как предлагает плагин, зависимость будет автоматически удалена из нашего build.gradle
.
Однако давайте вместо этого используем некоторую фиктивную логику с нашей зависимостью:
public static void main(String[] args) {
System.out.println("Hello world");
useGuava();
}
private static void useGuava() {
List<String> list = ImmutableList.of("Baledung", "is", "cool");
System.out.println(list.stream().collect(Collectors.joining(" ")));
}
Если мы перезапустим его, мы больше не получим ошибок:
$ ./gradlew lintGradle
BUILD SUCCESSFUL in 559ms
3 actionable tasks: 1 executed, 2 up-to-date
3.2. Использование транзитивных зависимостей
Давайте теперь включим другую зависимость:
dependencies {
implementation('com.google.guava:guava:29.0-jre')
implementation('org.apache.httpcomponents:httpclient:4.5.12')
testImplementation('junit:junit:4.12')
}
На этот раз давайте воспользуемся чем-нибудь из транзитивной зависимости:
public static void main(String[] args) {
System.out.println("Hello world");
useGuava();
useHttpCore();
}
// other methods
private static void useHttpCore() {
SSLContextBuilder.create();
}
Давай посмотрим что происходит:
$ ./gradlew lintGradle
> Task :lintGradle FAILED
# failure output omitted
warning unused-dependency one or more classes in org.apache.httpcomponents:httpcore:4.4.13
are required by your code directly (no auto-fix available)
warning unused-dependency this dependency is unused and can be removed
unused-dependencies/build.gradle:8
implementation('org.apache.httpcomponents:httpclient:4.5.12')
✖ 2 problems (0 errors, 2 warnings)
Получаем две ошибки. Первая ошибка примерно говорит, что мы должны напрямую ссылаться на httpcore
.
SSLContextBuilder в нашем образце на самом деле
является его частью.
Вторая ошибка говорит, что мы ничего не используем из httpclient.
Если мы используем транзитивную зависимость, плагин говорит нам сделать ее прямой .
Давайте взглянем на наше дерево зависимостей:
$ ./gradlew unused-dependencies:dependencies --configuration compileClasspath
> Task :unused-dependencies:dependencies
------------------------------------------------------------
Project :unused-dependencies - Gradle Unused Dependencies example
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- com.google.guava:guava:29.0-jre
| +--- com.google.guava:failureaccess:1.0.1
| +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
| +--- com.google.code.findbugs:jsr305:3.0.2
| +--- org.checkerframework:checker-qual:2.11.1
| +--- com.google.errorprone:error_prone_annotations:2.3.4
| \--- com.google.j2objc:j2objc-annotations:1.3
\--- org.apache.httpcomponents:httpclient:4.5.12
+--- org.apache.httpcomponents:httpcore:4.4.13
+--- commons-logging:commons-logging:1.2
\--- commons-codec:commons-codec:1.11
В этом случае мы видим, что httpcore
вводится httpclient
.
3.3. Использование зависимостей с отражением
Как насчет того, когда мы используем отражение?
Давайте немного улучшим наш пример:
public static void main(String[] args) {
System.out.println("Hello world");
useGuava();
useHttpCore();
useHttpClientWithReflection();
}
// other methods
private static void useHttpClientWithReflection() {
try {
Class<?> httpBuilder = Class.forName("org.apache.http.impl.client.HttpClientBuilder");
Method create = httpBuilder.getMethod("create", null);
create.invoke(httpBuilder, null);
} catch (Exception e) {
e.printStackTrace();
}
}
Теперь давайте перезапустим задачу Gradle:
$ ./gradlew lintGradle
> Task :lintGradle FAILED
# failure output omitted
warning unused-dependency one or more classes in org.apache.httpcomponents:httpcore:4.4.13
are required by your code directly (no auto-fix available)
warning unused-dependency this dependency is unused and can be removed
unused-dependencies/build.gradle:9
implementation('org.apache.httpcomponents:httpclient:4.5.12')
✖ 2 problems (0 errors, 2 warnings)
Что случилось? Мы использовали HttpClientBuilder
из нашей зависимости (httpclient),
но все равно получали ошибки.
Если мы используем библиотеку с отражением, плагин не определяет ее использование .
В итоге мы можем увидеть те же две ошибки.
В общем, мы должны настроить такие зависимости как runtimeOnly
.
3.4. Создание отчетов
Для больших проектов количество ошибок, возвращаемых терминалом, становится сложной задачей.
Давайте настроим плагин, чтобы он вместо этого выдавал нам отчет:
allprojects {
apply plugin :"java"
apply plugin :"nebula.lint"
gradleLint {
rules=['unused-dependency']
reportFormat = 'text'
}
// other details omitted
}
Давайте запустим задачу generateGradleLintReport
и проверим вывод нашей сборки:
$ ./gradlew generateGradleLintReport
# task output omitted
$ cat unused-dependencies/build/reports/gradleLint/unused-dependencies.txt
CodeNarc Report - Jun 20, 2020, 3:25:28 PM
Summary: TotalFiles=1 FilesWithViolations=1 P1=0 P2=3 P3=0
File: /home/user/tutorials/gradle-5/unused-dependencies/build.gradle
Violation: Rule=unused-dependency P=2 Line=null Msg=[one or more classes in org.apache.httpcomponents:httpcore:4.4.13
are required by your code directly]
Violation: Rule=unused-dependency P=2 Line=9 Msg=[this dependency is unused and can be removed]
Src=[implementation('org.apache.httpcomponents:httpclient:4.5.12')]
Violation: Rule=unused-dependency P=2 Line=17 Msg=[this dependency is unused and can be removed]
Src=[testImplementation('junit:junit:4.12')]
[CodeNarc (http://www.codenarc.org) v0.25.2]
Теперь он обнаруживает неиспользуемые зависимости в конфигурации testCompileClasspath
.
К сожалению, это непоследовательное поведение плагина. В результате теперь мы получаем три ошибки.
4. Вывод
В этом руководстве мы увидели, как найти неиспользуемые зависимости в сборках Gradle.
Сначала мы объяснили общую настройку. После этого мы изучили сообщения об ошибках с различными зависимостями и их использование.
Наконец, мы увидели, как создавать текстовые отчеты.
Как обычно, мы можем найти полные примеры кода на GitHub .