查找未使用的Gradle依赖

2025/04/30

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 = "cn.tuyucheng.taketoday"
    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

我们的compileClasspath配置中有一个未使用的依赖(guava)。

如果我们按照插件的建议运行fixGradleLint任务,依赖将自动从我们的build.gradle中删除

但是,让我们使用一些带有依赖关系的虚拟逻辑:

public static void main(String[] args) {
    System.out.println("Hello world");
    useGuava();
}

private static void useGuava() {
    List<String> list = ImmutableList.of("Tuyucheng", "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)

发生了什么?我们使用了依赖(httpclient)中的HttpClientBuilder,但仍然出现错误。

如果我们使用具有反射的库,插件将无法检测其使用情况

结果,我们可以看到相同的两个错误。

一般来说,我们应该配置诸如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构建中查找未使用的依赖。

首先,我们解释了常规设置。之后,我们探讨了不同依赖报告的错误及其用法。

最后,我们了解了如何生成基于文本的报告。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章