掘金 后端 ( ) • 2024-05-02 13:55

theme: github

1、使用插件的原因

使用插件是现代自动化构建工具中一个非常重要的概念,Gradle 作为其中一个流行工具,通过插件提供了多种便利。以下是使用插件的几个主要原因:

  1. 促进代码重用

    • 减少重复代码:插件允许你重用在不同项目中执行相同功能的代码,减少了编写和维护相似代码的需要。

    • 标准化流程:插件通常封装了一组标准化的操作,使得在多个项目间实现构建流程的一致性变得更加容易。

  2. 提升工作效率

    • 快速实现功能:插件通常提供了一组预定义的任务,可以快速实现打包、测试、部署等功能,从而加速开发流程。

    • 简化构建配置:通过应用插件,可以避免编写复杂的构建脚本,因为插件已经提供了所需的配置。

  3. 项目模块化

    • 解耦功能:插件使得特定的构建功能模块化,这样可以更容易管理和更新这些功能,而不会影响到项目的核心构建脚本。

    • 清晰的构建逻辑:模块化还有助于保持构建逻辑的清晰,使得构建脚本更易于理解和维护。

  4. 自动化和便捷化

    • 自动化流程:插件可以自动化许多繁琐的构建任务,让开发者专注于更重要的业务逻辑。

    • 用户友好:许多插件提供了便捷的命令和配置选项,使得构建过程更加用户友好。

  5. 扩展项目功能

    • 即插即用:插件的可插拔特性意味着你可以轻松地为项目添加新的功能,而无需深入了解实现细节。

    • 社区支持:Gradle 拥有一个活跃的社区,提供了大量的插件,可以扩展构建系统的功能,满足特定的构建需求。

  6. 维护和更新

    • 持续集成:许多插件支持持续集成(CI)流程,可以与 Jenkins、Travis CI 等工具集成,自动化测试和构建流程。

    • 易于更新:插件的更新通常比核心构建系统更容易,因为它们是独立的组件。

2、插件的作用

插件在 Gradle 构建系统中扮演着至关重要的角色,它们为项目提供了额外的功能和自动化能力。以下是插件在项目构建过程中可以完成的一些事情:

  1. 添加任务(Task)

    • 自定义任务:插件可以向项目中添加新的任务,这些任务可以是执行测试、编译源代码、打包应用程序等。

    • 任务配置:插件还可以预配置某些任务,使得开发者不需要手动设置所有参数。

  2. 添加依赖配置

    • 管理依赖:插件可以帮助定义项目的依赖管理,包括外部库依赖、项目内部模块依赖等。

    • 远程仓库:插件可以配置远程仓库,如 Maven Central 或 JCenter,以便下载所需的库。

  3. 扩展项目属性和方法

    • 扩展属性:插件可以向项目添加新的属性,这些属性可以用于控制构建过程或存储配置信息。

    • 扩展方法:插件可以向项目添加新的方法,这些方法可以用于执行特定的构建逻辑或封装复杂的操作。

  4. 项目约定

    • 目录结构:应用特定的插件,如 Java 插件,会为项目设置一些默认的目录结构约定,例如 src/main/java 作为源代码目录。

    • 构建生命周期:插件还可以定义构建过程中的特定阶段,例如编译、测试、打包和部署。

插件是 Gradle 强大的扩展机制,它们可以极大地简化构建配置,提供最佳实践,并允许开发者通过标准化的方式来构建项目。通过应用插件,你可以快速获得构建、测试、打包和依赖管理等自动化功能,同时保持项目的灵活性和可维护性。

3、插件的分类和使用

image-20240420202646427

3.1 脚本插件

在 Gradle 中,脚本插件是一种允许你通过外部 Groovy 脚本文件来扩展构建逻辑的方法。这些脚本可以定义变量、任务、配置等,并且可以通过 apply from 语句被导入到主构建脚本中。

3.1.1 脚本插件的创建

  1. 创建外部脚本文件:你可以创建一个包含 Groovy 代码的文件,比如 version.gradle
// version.gradle
ext {
    company = "阿杰"
    cfgs = [
            compileSdkVersion: JavaVersion.VERSION_21
    ]
    spring = [
            version: '6.0.0'
    ]
}

在这个脚本中,我们使用了 ext 来定义了一系列的属性,这些属性在导入脚本后可以在构建脚本中使用。

3.1.2 脚本插件的应用

  1. 在构建脚本中应用脚本插件:在你的 build.gradle 文件中,使用 apply from 来加载外部脚本。
// build.gradle
apply from: 'version.gradle'

task taskVersion {
    doLast {
        println "博主的昵称:${company}, JDK版本是${cfgs.compileSdkVersion}, 版本号是${spring.version}"
    }
}

在这个构建脚本中,我们应用了之前创建的 version.gradle 脚本。然后我们定义了一个名为 taskVersion 的任务,它会在执行时打印出从脚本插件中导入的变量。

  1. 运行构建:在命令行中运行 Gradle 构建,执行 taskVersion 任务。
gradle taskVersion

image-20240420203619061

执行上述命令后,Gradle 会加载并执行 build.gradle 文件中的指令,包括应用外部脚本插件和执行 taskVersion 任务。

3.1.3 注意事项

  • 脚本位置:确保外部脚本文件的位置是正确的,并且 Gradle 能够找到它。
  • 安全性:从网络上加载脚本时要注意安全性,确保脚本来源可靠,避免执行恶意代码。

总结

脚本插件是 Gradle 提供的一种灵活的扩展机制,允许你通过外部 Groovy 脚本来添加构建逻辑。这种方式非常适合于在多个项目之间共享配置或创建自定义的构建行为。通过使用脚本插件,可按功能把我们的脚本进行拆分一个个公用、职责分明的文件,然后在主脚本文件引用,

比如:将很多共有的库版本号一起管理、应用构建版本一起管理等。可以使构建脚本更加模块化和可重用。

3.2 对象插件

Gradle 的对象插件,也称为二进制插件或 Java 插件,是通过实现 org.gradle.api.Plugin 接口的 Java 类来提供的。这些插件为项目提供了特定的构建功能,并且可以通过一个唯一的插件 ID 来应用到项目中。

image-20240420204109332

3.2.1 内部插件[核心插件]

Gradle 提供了一系列的核心插件,这些插件覆盖了多种常见的构建需求,包括但不限于:

  • Java:支持构建任何类型的 Java 项目。
  • C++ Application:支持在 Windows、Linux 和 macOS 上构建 C++ 应用程序。
  • Application:支持构建基于 JVM 的可运行应用程序。
  • WAR:支持构建和打包基于 WAR 的 Java Web 应用程序。
  • Checkstyle:使用 Checkstyle 对项目中的 Java 源文件执行质量检查。
  • Eclipse:为构建生成 Eclipse 项目文件。
  • IntelliJ IDEA:为构建生成 IDEA 项目文件。

查找核心插件:

要查找 Gradle 提供的所有核心插件,可以访问官方文档中的插件参考页面:

在这里,你可以找到每个插件的描述、提供的配置和如何应用该插件的指导。

使用 Java 插件

以下是几种应用 Java 插件到项目中的方法:

  1. 使用插件 ID

    apply plugin: 'java'
    

    这是应用 Java 插件的推荐方式,简洁且易于理解。

  2. 使用闭包

    apply {
        plugin 'java'
    }
    

    这种方式使用了一个闭包来包裹插件 ID,这在需要应用多个插件时非常有用。

  3. 使用全类名

    apply plugin: org.gradle.api.plugins.JavaPlugin
    

    这种方式直接指定了插件的完整类名,这在 IDE 中自动完成代码时可能会看到。

  4. 使用导入的类

    apply plugin: JavaPlugin
    

    如果 org.gradle.api.plugins 包已经被导入,你可以直接使用类名来引用插件。

插件 ID 的唯一性:

每个插件都有一个唯一的插件 ID,这有助于避免名称冲突。通常,插件 ID 会采用应用包名来确保其唯一性,如 java 对应的完整类名为 org.gradle.api.plugins.JavaPlugin

3.2.2 第三方插件

在使用第三方发布的二进制插件时,通常需要确保 Gradle 能够从正确的仓库中获取这些插件。以下是两种常见的方法来应用这些插件:

1. 传统应用方式

使用 buildscript 块,你需要指定插件的类路径和仓库:

buildscript {
    ext {
        springBootVersion = "2.3.3.RELEASE"
    }
    repositories {
        mavenLocal()
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
        jcenter()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'org.springframework.boot'

在这个例子中,我们首先定义了 buildscript 块,它告诉 Gradle 从哪里查找插件。repositories 块可以包含多个仓库,这里包括了 Maven 本地仓库、阿里云的 Maven 仓库和 JCenter。

2. plugins DSL 方式

Gradle 引入了一种新的 plugins 块,它提供了一种更简洁的方式来应用插件,特别是当插件被托管在 Gradle 插件仓库 时:

plugins {
    id 'org.springframework.boot' version '2.4.1'
}

使用 plugins 块时,Gradle 会自动从 Gradle 插件仓库中解析插件的坐标,因此你不需要显式地指定类路径和仓库。

3. 注意事项

  1. 位置:在使用传统方式时,buildscript 块必须位于 build.gradle 文件的顶部。而 plugins 块则没有这个限制,可以放在文件的任何位置。

  2. 格式plugins 块使用 idversion 来指定插件的 ID 和版本,这使得构建脚本更加清晰和易于管理。

  3. 自动解析:当使用 plugins 块时,Gradle 会自动解析插件的坐标,这意味着你不需要手动添加仓库和类路径。

  4. 兼容性:确保你使用的 Gradle 版本与第三方插件的版本兼容。

总结

使用 plugins 块可以简化插件的应用过程,特别是当你使用托管在 Gradle 插件仓库中的插件时。这种方式使得构建脚本更加简洁,同时减少了配置错误的可能性。然而,在一些情况下,你可能仍然需要使用传统的 buildscript 方法,比如当插件不是托管在 Gradle 插件仓库中,或者你需要自定义插件的类路径和仓库时。

3.2.3 用户自定义插件

在 Gradle 中,用户自定义插件是一种强大的扩展构建功能的方式。自定义插件允许你封装逻辑,创建新的任务,甚至向项目添加自定义的扩展属性和方法。这些插件可以用于单个项目,也可以被设计为可重用和可共享的。

1. 示例

/**
 * GreetingPluginExtension 接口定义了问候插件的扩展属性。
 * 这个接口规定了插件配置中可以使用的两个属性:getMessage 和 getGreeter。
 */
interface GreetingPluginExtension {
    // getMessage 方法:获取问候语。
    Property<String> getMessage()
    // getGreeter 方法:获取发出问候的实体。
    Property<String> getGreeter()
}

/**
 * GreetingPlugin 类实现了 Plugin 接口,为 Gradle 提供了一个问候插件。
 * 这个插件向项目中添加了一个 'hello' 任务,该任务在执行时会打印一条问候语。
 */
class GreetingPlugin implements Plugin<Project> {
    /**
     * apply 方法将插件应用到指定的项目上。
     * @param project 要应用插件的项目实例。
     */
    void apply(Project project) {
        // 创建一个名为 'greeting' 的插件扩展,允许用户自定义问候语和发出问候的实体。
        def extension = project.extensions.create('greeting', GreetingPluginExtension)

        // 创建一个名为 'hello' 的任务,任务执行时会打印配置的问候语。
        project.task('hello') {
            doLast {
                // 打印问候语,使用插件扩展中配置的消息和发出问候的实体。
                println "${extension.message.get()} from ${extension.greeter.get()}"
            }
        }
    }
}

// 应用 GreetingPlugin 插件到当前的 Gradle 项目中。
apply plugin: GreetingPlugin

// 配置插件扩展,设置问候语和发出问候的实体。
greeting.message = 'Hello from plugin'
greeting.greeter = 'Gradle'

image-20240420212341025

2. 用户自定义插件的局限性

虽然用户自定义插件非常灵活,但它们通常局限于定义它们的项目。这意味着,如果你在一个项目中定义了一个自定义插件,它可能不适用于其他项目,除非你将插件逻辑提取到一个独立的类或 JAR 文件中,并在其他项目中包含这个类或 JAR 文件。

3. 共享自定义插件

为了使自定义插件可重用和共享,你可以:

  • 构建独立插件:将插件代码放入一个独立的项目中,构建为 JAR 文件,然后发布到本地仓库或远程仓库。
  • 使用 buildSrc:对于小型或特定于项目的插件,可以将插件代码放置在项目的 buildSrc 目录中。这样,插件可以被当前项目和子项目使用。

总结

自定义插件是 Gradle 插件系统的重要组成部分,它们允许开发者根据自己的需求扩展构建过程。通过创建自定义插件,你可以增加新的构建任务,定义配置选项,甚至提供全新的构建逻辑。自定义插件的开发需要对 Gradle 插件机制有深入的理解,并且需要遵循一定的设计原则以确保插件的可重用性和可维护性。

4、buildSrc 项目

buildSrc 是 Gradle 提供的一个特殊目录,用于存放构建过程中使用的自定义 Groovy 插件和脚本。这个目录被视为一个独立的 Gradle 项目,你可以在这个目录下编写自己的插件和脚本,它们可以在整个构建过程中被重用。

以下是如何使用 buildSrc 目录的步骤:

4.1 创建 buildSrc 模块

。

将 buildSrc 从 included modules 移除,重新构建。

image-20240502100124262

然后只保留 build.gradle 和 src/main 目录,其他全部删掉

image-20240502100230541

4.2 然后修改 Gradle 中的内容

plugins {
    id 'java'
    id 'groovy' // 必须
    id 'maven-publish'
    id 'java-gradle-plugin' // 必须
}

group = 'com.jie'
version = '1.0'

repositories {
    google()
    jcenter()
    mavenCentral() //必须
}

// 项目入口 插件可以使用java groovy kotlin 三种语言进行编写
sourceSets {
    main {
        groovy {
            srcDir 'src/main/groovy'
        }
        java {
            srcDir 'src/main/java'
        }
    }
}

image-20240502101755522

4.3 创建入口目录,在 src/main 下创建代码入口目录

image-20240502101040546

4.4 实现插件代码

package com.jie

import org.gradle.api.Plugin
import org.gradle.api.Project

class Txt implements Plugin<Project> {
    @Override
    void apply(Project project) {
        project.task("testPlugin") {
            doLast {
                println("Hello,Gradle Plugin")
            }
        }
    }
}

4.5 配置插件入口

// 插件入口
gradlePlugin {
    plugins {
        DependenciesPlugin {
            id = 'com.jie.testPlugin'
            implementationClass = 'com.jie.Txt'
        }
    }
}

image-20240502102521585

4.6 在 module 引入我们写的插件

image-20240502102601436

然后执行插件的Task。

image-20240502102704637

这种形式的写法,在我们整个工程的 module 都可以使用,但也只是限制在本工程,其他工程不能使用。

5、编写在单独项目中

有时候我们需要一个插件在多个工程中使用,这时候我们就需要把插件上传 maven 中。

5.1 新建Module

新建一个名称为plugin的Module。

image-20240502103519070

image-20240502103344774

Gradle Plugin可以用java写,也可以用kotlin、groovy,喜欢用什么就可以在main文件下新建对应语言的文件夹接口,比如groovy文件夹。

5.2 新建文件添加依赖

5.2.1 添加依赖

image-20240502104334240

5.2.2 添加类

之前是用的Groovy写,这次用 Java。

image-20240502104420578

5.2.3 添加插件入口

// 插件入口
gradlePlugin {
    plugins {
        DependenciesPlugin {
            id = 'com.jie.plugin.txtPlugin'
            implementationClass = 'com.jie.plugin.Txt'
        }
    }
}

image-20240502111914030

到此,Plugin的基本雏形就有了。

5.3 发布插件

5.3.1 添加依赖

首先,比较常用的仓库是maven,在plugin>build.gradle文件中先依赖一个maven发布的插件maven-publish

image-20240502104703983

5.3.2 添加发布配置

publishing {
    publications {
        myLibrary(MavenPublication) {
            groupId = 'com.jie.plugin' //指定GAV坐标信息
            artifactId = 'txt'
            version = '1.0'

            from components.java    //发布jar包
        }
    }

    repositories {
        maven {
            url "$rootDir/lib/release"
        }

        //发布项目到私服中
//    maven {
//        name = 'myRepo' //name属性可选,表示仓库名称,url必填
//        //发布地址:可以是本地仓库或者maven私服
//        //url = layout.buildDirectory.dir("repo")
//        //url='http://my.org/repo' // // change URLs to point to your repos, e.g. http://my.org/repo // //认证信息:用户名和密码
//        credentials {
//            username = 'joe'
//            password = 'secret'
//        }
//    }
    }
}

image-20240502105217729

5.3.3 执行发布操作

image-20240502110731057

image-20240502111207922

没有出现可能是idea 没有加载出来,刷新一下项目即可。

image-20240502111304684

5.3.4 使用

在settings.gradle文件中配置插件仓库地址

pluginManagement {
    repositories {
        maven {
            url "$rootDir/lib/release"
        }
    }
}

image-20240502111441643

在project>build.gradle文件中添加插件依赖

buildscript {
    dependencies {
        classpath('com.jie.plugin:txt:1.0')
    }
}

image-20240502112020994

再建一个module进行使用。

image-20240502112406958

编译看效果:

image-20240502112520352

在控制台看到自定义插件的输出,说明自定义插件就已经生效了。

最后,至于如何写一个插件,能帮助项目更加自动化或者便捷化,是值得大家未来需要长期思考、关注、努力的点。

6、插件的关注点

6.1 第一点:插件的引用

apply plugin: '插件名' 

6.2 第二点:主要的功能[任务]

当我们在工程中引入插件后,插件会自动的为我们的工程添加一些额外的任务来完成相应的功能。以 Java 插件为例,当

我们加入 java 插件之后,就加入了如下功能:

image-20240502112825658

具体大家可通过 gradle tasks 查看加入某个插件前后的区别。

说明:Gradle 中的任务依赖关系是很重要的,它们之间的依赖关系就形成了构建的基本流程。

6.3 第三点:工程目录结构

一些插件对工程目结构有约定,所以我们一般遵循它的约定结构来创建工程,这也是 Gradle 的“约定优于配置”原则。

例如 java 插件规定的项目源集目录结构如下所示:

image-20240502112922363

如果要使用某个插件就应该按照它约定的目录结构设置,这样能大大提高我们的效率,当然各目录结构也可以自己定义

6.4 第四点:依赖管理

比如前面我们提到的 依赖的类型[依赖管理]部分,不同的插件提供了不同的依赖管理。

6.5 第五点:常用的属性

例如:Java 插件会为工程添加一些常用的属性,我们可以直接在编译脚本中直接使用。

属性名称 类型 默认值 描述 reportsDirName String reports 生成报告的目录名称 reportsDir File (只读) buildDir/reportsDirName 生成报告的目录 testResultsDirName String test-results 生成测试.result.xml文件的目录名称 testResultsDir File (只读) reportsDir/testReportDirName 生成测试报告的目录 libsDirName String libs 生成 lib 库的目录名称 libsDir File (只读) buildDir/libsDirName 生成 lib 库的目录 distsDirName String distributions 生成发布文件的目录名称 distsDir File (只读) buildDir/distsDirName 生成发布文件目录 docsDirName String docs 生成帮助文档的目录名称 docsDir File (只读) buildDir/docsDirName 生成帮助文档的目录 dependencyCacheDirNameString String dependency-cache 存储缓存资源依赖信息的目录名称 dependencyCacheDir File (只读) buildDir/dependencyCacheDirName 存储缓存资源依赖信息的目录

7 、Java 插件分析

参考官网:https://docs.gradle.org/current/userguide/plugin_reference.html,以 Java 插件为例,讲解需要关注的几点:

7.1 第一点:我们要关注插件使用

plugins {
	id 'java' 
}

7.2 第二点:我们要关注插件的功能

我们可通过官方文档介绍了解某个插件功能或者百度、再或者大家可以通过 gradle tasks 查看加入 java 插件前后的区别。

7.3 第三点:项目布局

一般加入一个插件之后,插件也会提供相应的目录结构,例如:java 插件的目录结构

image-20240502113555425

当然这个默认的目录结构也是可以改动的例如:

sourceSets {
    main {
        java {
            srcDirs = ['src/java']
        }
        resources {
            srcDirs = ['src/resources']
        }
    }
}

也可设置源集的属性等信息。

7.4 第四点:依赖管理:以 java 插件为例,提供了很多依赖管理项

image-20240502113716311