Menu Close

使用 Gradle 快速构建 Java 项目(例二)

环境描述

描述项 内容
操作系统 CentOS Linux release 7.8.2003 (Core)
java版本 java version "1.8.0_161"
IDEA版本 2022.2.2
Gradle版本 7.5

Gradle 默认内建了一个 init 插件,可以生成 Java 项目基础结构

具体命令如下:

 $] gradle init --type <name>

其中的 name 可以是以下值

  • java-application
  • java-library
  • scala-library
  • groovy-library
  • basic

这里选择java-application类型,具体步骤如下:

1. 创建目录

mkdir javaDemo
cd javaDemo

file

2. 初始化项目

gradle init --type java-application

这里直接回车,全部使用默认配置

file

2.1. gradle init 执行成功之后的目录结构如下

.
├── app
│   ├── build.gradle
│   └── src
│       ├── main   # 源代码目录
│       │   ├── java
│       │   │   └── javademo
│       │   │       └── App.java
│       │   └── resources
│       └── test   # 测试代码目录
│           ├── java
│           │   └── javademo
│           │       └── AppTest.java
│           └── resources
├── gradle         #wrapper的文件
│   └── wrapper
│       ├── gradle-wrapper.jar  # 具体业务逻辑
│       └── gradle-wrapper.properties  # 配置文件
├── gradlew      # gradlew是一个shell 脚本,Unix 用户可以通过它来执行 Gradle 任务。
├── gradlew.bat  # gradlew.bat是bat 脚本,Windows 用户可以通过它执行 Gradle 任务,配合gradle文件夹使用。
└── settings.gradle

12 directories, 8 files

2.2 settings.gradle文件

/*
 * This file was generated by the Gradle 'init' task.
 *
 * The settings file is used to specify which projects to include in your build.
 *
 * Detailed information about configuring a multi-project build in Gradle can be found
 * in the user manual at https://docs.gradle.org/7.5/userguide/multi_project_builds.html
 */

rootProject.name = 'javaDemo'
include('app')

rootProject.name就是指定项目名称的,gradlew build生成的 jar 包就是[项目名称].jar。

另外 setting.gradle 用来告诉 gradle 这个项目还包含了那些子项目。

2.3. app/build.gradle 文件

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java application project to get you started.
 * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
 * User Manual available at https://docs.gradle.org/7.5/userguide/building_java_projects.html
 */

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

/**
  * 指定仓库的路径
  * mavenCentral():表示使用中央仓库,即项目中的jar会从中央仓库下载到本地指定目录中
  * C:/Users/Administrator/.gradle(可以在setting中的gradle中的service directory path进行自定义配置)
  */
repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

/**
 * gradle工程所有的jar包的坐标都在dependencies属性内放置
 * 每一个jar包的坐标 都有3个基本元素:
 * group,name,version
 * 类似于maven中的 <groupId>,<artifactId>,<version>
 * testImplementation:表示该jar包在测试的时候起作用,该属性为jar包的作用域
 * 所以我们在添加jar包坐标的时候,都要带上jar包的作用域
 */
dependencies {
    // Use JUnit Jupiter for testing. junit测试包
    testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'

    // This dependency is used by the application. google的guava包
    implementation 'com.google.guava:guava:31.0.1-jre'
}

application {
    // Define the main class for the application. main类
    mainClass = 'javademo.App'
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

build文件中加了java和application两个插件。前者支持java项目,后者允许指定包含main方法的类,在run时执行。

2.4. app/src/main/java/javademo/App.java 代码

/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package javademo;

public class App {
    public String getGreeting() {
        return "Hello World!";
    }

    public static void main(String[] args) { # 在运行application插件的run task 时调用
        System.out.println(new App().getGreeting());
    }
}

2.5. app/src/test/java/javademo/AppTest.java

/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package javademo;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class AppTest {
    @Test void appHasAGreeting() {
        App classUnderTest = new App();
        assertNotNull(classUnderTest.getGreeting(), "app should have a greeting");
    }
}

2.6. gradle/wrapper/gradle-wrapper.properties

# 解压gradle的总目录
distributionBase=GRADLE_USER_HOME

# 解压gradle总目录下的目录
distributionPath=wrapper/dists

#下载gradle的地址,和版本
# 1. gradle-xx-all.zip是完整版,包含了各种二进制文件,源代码文件,和离线的文档。
# 2. gradle-xx-bin.zip是二进制版,只包含了二进制文件(可执行文件),没有文档和源代码。
# 3. gradle-xx-src.zip是源码版,只包含了Gradle源代码,不能用来编译你的工程。
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip

# 下载gradle的总目录
zipStoreBase=GRADLE_USER_HOME

# 下载gradle总目录下的目录
zipStorePath=wrapper/dists

gradle-wrapper.properties文件主要指定了该项目需要什么版本的Gradle,从哪里下载该版本的Gradle,下载下来放到哪里。从图示项目中可以知道我要使用gradle-7.5版本。

我们使用gradlew命令的使用,会根据这个文件来使用对应的gradle进行构建。当本地GRADLE_USER_HOME(当前用户目录,一般指 ~/.gradle)中的 ~/.gradle/wrapper/dists 没有安装gradle时,将会自动从此地址distributionUrl中下载gradle,之后的执行将不会再次下载安装。

GRADLE_USER_HOME:环境变量(类似于JAVA_HOME),gradle的用户目录,里面放gradle的相关配置等,类似于maven的.m2的文件路径。

  • Mac的默认目录是:/Users/xxx/.gradle
  • Windows的默认目录是:C:/用户/xxx/.gradle

使用该文件的步骤如下:

  1. 解析gradle-wrapper.properties文件,获取项目需要的 gradle 版本下载地址。
  2. 判断本地用户目录下的 ~/.gradle 目录下是否存在该版本,不存在该版本,走第3点,存在走第4点。
  3. 下载gradle-wrapper.properties指定版本,并解压到用户目录的下 ~/.gradle 文件下。
  4. 利用 ~/.gradle 目录下对应的版本的 gradle 进行相应自动编译操作。

3. 编译项目

现在有 2 种方式来编译这个示例项目,分别是

  1. 通过本机安装好的 gradle 命令编译
  2. 通过当前项目根目录下的 gradlew 命令编译(官方推荐的使用方式)

这里使用方式2进行编译:

$ ./gradlew build
:compileJava
// Download of Guava if not already cached...
:processResources UP-TO-DATE
:classes
:jar
:startScripts
:distTar
:distZip
:assemble
:compileTestJava
// Download of JUnit if not already cached...
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

第一次运行构建时,Gradle会检查您的缓存中是否已经有Guava和JUnit库 ~/.gradle。如果没有,库将被下载并存储在那里。下次运行构建时,将使用缓存的版本。该build任务编译的类,运行测试,并生成测试报告。

您可以通过打开位于的HTML输出文件来查看测试报告 app/build/reports/tests/test/index.html

file

4. 运行项目

因为Gradle构建使用了Application plugin,所以可以从命令行运行应用程序。首先,使用tasks任务来看看插件添加了哪些任务。

$ ./gradlew tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Application tasks
-----------------
run - Runs this project as a JVM application

// ... many other tasks ...

run task会让gradle读取mainClassName属性指定的类,执行main方法

$ ./gradlew run
:compileJava UP-TO-DATE
:processResources NO-SOURCE
:classes UP-TO-DATE
:run
Hello world.

BUILD SUCCESSFUL

附录

A. 相关联的文章

B. gradle proxy 代理配置

参考: https://blog.csdn.net/dalich/article/details/125166316

在gradle工程中,最好在以下两个文件中,均添加以上配置:

  • ./gradle.properties
  • ./gradle/wrapper/gradle-wrapper.properties

主要格式如下:

systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=1080

C. 仓库地址换成阿里

  • build.gradle(Groovy语法)

    repositories {
    maven { url 'https://maven.aliyun.com/repository/jcenter' }
    maven { url 'https://maven.aliyun.com/repository/google' }
    maven { url 'https://maven.aliyun.com/repository/central' }
    maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
    }
  • build.gradle.kts(Kotlin语法)

    repositories {
    maven { setUrl( "https://maven.aliyun.com/repository/jcenter") }
    maven { setUrl( "https://maven.aliyun.com/repository/google") }
    maven { setUrl( "https://maven.aliyun.com/repository/central") }
    maven { setUrl( "https://maven.aliyun.com/repository/gradle-plugin") }
    }
  • Kotlin语法也可以简写

    repositories {
    maven ("https://maven.aliyun.com/repository/jcenter")
    maven ( "https://maven.aliyun.com/repository/google")
    maven ( "https://maven.aliyun.com/repository/central")
    maven ( "https://maven.aliyun.com/repository/gradle-plugin")
    }

D. Gradle 基本原理

我们知道 Gradle 是一种以 Groovy 语言为基础的自动化构建工具,一般通过修改 build.gradle 脚本来完成对项目构建的一些设置,例如依赖管理等等。大多数情况下,我们只需要稍微修改下 gradle 文件即可完成自己的需求。

自动化构建工具听起来似乎比较复杂,本质上来说也是一种程序,跟我们自己写的代码一样,我们开始编译时就启动这个程序,然后读取我们在 gradle 文件中配置的参数来实例化各个类,然后按照顺序依次执行对应的任务即可完成整个构建任务。

所以 build.gradle 文件,或者其他后缀为 gradle 的文件其实就是个配置文件,就好像 xml 一样,我们在 gradle 文件中修改各种配置参数,Gradle 通过这些参数来实例化 Project 等等就像构造器一样,只要理解了这点学习 Gradle 就会变得很容易。

当我们新建一个项目后,Gradle 默认会生成一些编译脚本文件,主要有:setting.gradle、build.gradle 以及子项目中的 build.gradle 等等,还会在当前目录下生成一个 gradle 文件夹,下面分别介绍一些这些文件的作用:

  • setting.gradle 用来告诉 gradle 这个项目包含了那些子项目。

  • build.gradle 是默认的构建脚本,当我们在执行 gradle 命令时,会首先到当前目录下寻找该文件,然后通过该文件的配置实例化一个 Project 对象。

  • 自动生成的 gradle 文件夹是 Gradle 包装器,其中包含一个 jar 文件和一个 配置文件,使用这个包装器可以让 Gradle 运行在一个特定的版本上,目的是创造一个独立于系统、系统配置和 Gradle 版本的可靠和可重复构建。

Gradle 中有两个重要的概念,分别是 Project 和 Task,Project 表示一个正在构建的项目,而 Task 表示某一个具体的任务。

  1. Project
    Project 表示正在构建的项目,每个 Project 都对应一个我们在 setting.gradle 中配置的 Project,除此之外还有一个 Root Project。

  2. Task
    Task 定义了一个当前任务执行时的最小单元,一个 Project 中一般会存在很多个 Task,通过执行这些 Task 来完成整个项目的构建。也就是说,项目构建的实际工作是由一个个的 Task 来完成的。Task有下列特征:可以依赖于别的 Task;不同时机的 Task 动作:onConfig(配置块)、doFirst、doLast、action;输入/输出;一些 getter/setter 属性。

E. Gradle中的sourceCompatibility

Gradle中有两个属性,分别是sourceCompatibility和targetCompatibility。
简单的说,sourceCompatibility属性跟编译环境有关,而targetCompatibility属性跟运行环境有关。

至少有这么几个原则,是不能违背的:

  1. sourceCompatibility关系到你使用到的Java语法特性及库
  2. sourceCompatibility不能比targetCompatibility大
  3. targetCompatibility不能比目标客户端运行环境的JavaVersion大
  4. targetCompatibility不能比当前Gradle使用的JavaVersion大

总结起来就是这样

代码用的语言特性对应的JavaVersion
≦ sourceCompatibility
≦ targetCompatibility
≦ Gradle使用的JavaVersion
≦ 客户端环境的JavaVersion

F. Gradle 生命周期

  1. 初始化
    在初始化阶段,Gradle 会解析 setting.gradle 文件获取该项目包含几个子项目,然后创建一个 RootProject 以及分别为子项目创建 Project 实例。

  2. 配置
    初始化完成后进入配置阶段,此时会加载所有 build.gradle 文件配置及插件,然后执行所有 Task 的配置代码块。

  3. 执行
    执行指的就是依据顺序执行所有 Task 的动作。

需要注意,无论我们是单独运行某一个 Task,还是运行所有的 Task,Gradle 的生命周期都是固定为上述的三个步骤,只不过执行的时候会有选择的执行指定 Task 及其依赖的 Task,这意味着如果一个 Task 设置了在配置阶段执行某项任务,即使我们运行了别的 Task,该任务也会被执行。

file