自学内容网 自学内容网

DevOps工程技术价值流:项目构建工具的选择与实践

在快速迭代的软件工程领域,项目构建工具扮演着举足轻重的角色。它们不仅自动化了构建、测试、打包和部署等关键环节,还显著提升了开发效率和质量。本文将深入探讨后端常用的Maven和Gradle,以及前端不可或缺的NPM,并重点对比Maven与Gradle的优劣,为您的项目构建工具选择提供有力参考。

一、Maven构建:Java项目的基石

Maven,作为Apache软件基金会的明星项目,专为Java项目提供构建和依赖管理支持。其核心在于约定的目录结构,这一结构确保了Maven能够精准定位项目资源,实现高效、准确的自动化构建。

  • 目录结构约定:Maven规定了标准的项目目录结构,如src/main/java存放Java源文件,src/test/java存放测试代码等。这种约定不仅简化了构建过程,还提高了项目的可维护性。

  • 依赖管理:Maven通过pom.xml文件管理项目依赖,支持自动下载和更新依赖库,避免了手动管理依赖的繁琐和错误。

  • 生命周期管理:Maven定义了清晰的构建生命周期,包括编译、测试、打包、部署等阶段,每个阶段都有明确的任务和顺序,确保构建过程的可控性和可预测性。

基于第三方工具或框架的约定 Maven 对工程目录结构的要求:

my-maven-project
|-- src
|   |-- main
|   |   |-- java       # Java 源代码目录
|   |   |-- resources  # 资源文件目录(如配置文件)
|   |-- test
|       |-- java       # 测试代码目录
|       |-- resources  # 测试资源文件目录
|-- pom.xml            # Maven 项目对象模型文件,用于配置项目信息、依赖等
|-- target             # 构建过程中生成的文件(如编译后的字节码文件、打包后的 JAR 文件等)

1 构建过程

在 Java 项目开发过程中,构建指的是使用“原材料生产产品”的过程。Maven 的构建过程主要包含以下环节:

  1. 清理(clean):删除构建过程中生成的文件,如 target 目录下的内容。

  2. 编译(compile):将 Java 源代码编译成字节码文件(.class 文件)。

  3. 测试(test):运行测试代码,验证项目的正确性。

  4. 打包(package):将编译后的字节码文件和资源文件打包成 JAR 或 WAR 文件。

  5. 安装(install):将打包后的文件安装到本地仓库中,供其他项目使用。

  6. 部署(deploy):将打包后的文件部署到远程仓库中,供其他团队或项目使用。

这些环节可以通过 Maven 的生命周期进行管理,每个生命周期阶段都有对应的目标(goal)可以执行。

2 依赖管理

Maven 中最关键的部分是依赖管理。使用 Maven 最主要的就是使用它的依赖管理功能,它可以帮助我们解决以下问题:

  1. jar 包的下载:使用 Maven 之后,jar 包会从规范的远程仓库(如 Maven 中央仓库)下载到本地仓库中。这样,我们就无需手动下载和管理这些 jar 包了。

  2. jar 包之间的依赖:通过依赖的传递性自动完成。例如,如果项目 A 依赖项目 B,而项目 B 又依赖项目 C,那么 Maven 会自动下载并引入项目 C 的 jar 包。

  3. jar 包之间的冲突:在实际开发中,可能会遇到多个 jar 包包含相同类或接口的情况,这会导致类路径冲突。Maven 提供了多种机制来解决这个问题,如排除(exclusion)、版本仲裁(version mediation)等。通过对依赖的配置进行调整,我们可以让某些 jar 包不会被导入,从而解决冲突问题。

3 Maven 开发环境配置

3.1检查jdk安装

在安装 Maven 之前,请确保你已经正确安装了 JDK。Maven 支持 JDK 1.4 及以上的版本,但本书的所有样例都基于 JDK 5 及以上版本。你可以通过打开 Windows 的命令行,并运行 java -version 命令来检查你的 Java 安装情况:

3.2 下载Maven

请访问 Maven 的官方下载页面(Download Apache Maven – Maven),下载适合你的操作系统的 Maven 版本。对于初学者,推荐使用 Maven 3.x 的稳定版本。下载页面还提供了 MD5 校验和文件及 ASC 数字签名文件,用于验证下载的 Maven 分发包的正确性和安全性。

3.3 本地安装与配置环境变量

  1. 将下载的 Maven 安装文件解压到你指定的目录中,例如 D:\bin\apache-maven-3.x

  2. 设置环境变量:

    1. 新建一个系统变量,变量名为 M2_HOME,变量值为 Maven 的安装目录,例如 D:\bin\apache-maven-3.x

    2. 在系统变量中找到名为 Path 的变量,并在其值的末尾添加 %M2_HOME%\bin;(注意分号分隔)。

配置完成后,你可以通过运行 mvn -v 命令来验证 Maven 是否安装成功。

详细情况如图所示:

3.4 配置基础 JDK 版本

Maven 默认使用 JDK 1.5 进行编译,但你可以通过修改 settings.xml 文件来指定其他版本的 JDK。在 settings.xml 文件的 <profiles> 标签内添加以下配置,以使用 JDK 1.8:

<profile>
    <id>jdk-1.8</id>
    <activation>
        <activeByDefault>true</activeByDefault>
        <jdk>1.8</jdk>
    </activation>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    </properties>
</profile>

通过 mvn -v 验证:

3.5 指定本地仓库

Maven 的本地仓库默认位于用户家目录下的 .m2/repository。为了避免影响系统性能,建议将本地仓库移动到其他盘符。你可以在 settings.xml 文件中添加以下配置来指定本地仓库的位置:

<localRepository>D:\mlz\maven-repository</localRepository>

3.6 配置镜像仓库

为了提高下载速度,你可以将 Maven 的中央仓库替换为国内的镜像仓库,如阿里云提供的镜像。在 settings.xml 文件中添加以下配置:

<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>central</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

3.7 升级Maven

Maven 更新频繁,你可以通过下载新的 Maven 安装文件并解压到本地目录来更新 Maven。然后,更新 M2_HOME 环境变量以指向新的 Maven 安装目录。

4 Maven 的使用

4.1 核心概念:坐标

Maven 中的坐标使用三个“向量”在 Maven 的仓库中唯一地定位到一个 JAR 包。这三个向量分别是:

  • groupId:公司或组织的唯一标识符,通常使用公司或组织域名的倒序,并可能加上项目名称。例如:com.mlz.team

  • artifactId:项目或项目中的一个模块的标识符,即模块的名称。例如:team-plan

  • version:版本号,用于区分同一个项目或模块的不同版本。例如:1.0.0

这三个坐标组合起来,可以唯一地确定一个 JAR 包在 Maven 仓库中的位置。例如,上述坐标对应的 JAR 包在 Maven 本地仓库中的位置为:

Maven本地仓库根目录\com\mlz\team\team-plan\1.0.0\team-plan-1.0.0.jar

4.2 pom.xml 文件详解

POM(Project Object Model)是 Maven 工程的核心配置文件,它表示将工程抽象为一个模型,并用程序中的对象来描述这个模型。通过 POM 文件,我们可以使用程序来管理项目。

以下是一个典型的 pom.xml 文件的配置示例,并对每个部分进行了详细的解释:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- 当前Maven工程的坐标 -->
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Maven</description>

    <!-- 当前Maven工程的打包方式 -->
    <packaging>jar</packaging>

    <!-- 工程构建过程中使用的属性 -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source> <!-- Java源码版本 -->
        <maven.compiler.target>1.8</maven.compiler.target> <!-- Java目标版本 -->
    </properties>

    <!-- 当前工程所依赖的jar包 -->
    <dependencies>
        <!-- 使用dependency配置一个具体的依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope> <!-- 依赖范围:仅在测试时使用 -->
        </dependency>
        
        <dependency>
            <groupId>com.mlz.team</groupId>
            <artifactId>team-plan</artifactId>
            <version>1.0.0</version>
            <scope>compile</scope> <!-- 依赖范围:在编译、测试和运行阶段都可用 -->
            
            <!-- 使用excludes标签配置依赖的排除 -->
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <!-- 构建配置 -->
    <build>
        <plugins>
            <!-- 配置编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.3 继承

在大型 Maven 项目中,管理和维护多个模块的依赖、插件和构建配置可能会变得非常复杂。为了简化这一过程,Maven 提供了继承机制,允许我们创建一个父 POM 来统一管理和配置多个子模块。这样做不仅有助于保持项目的一致性,还能提高开发效率。

4.3.1 创建父工程
  1. 使用 Maven 命令或 IDE 创建一个新的 Maven 项目。

  2. pom.xml 文件中,将 <packaging> 标签的值设置为 pom,表示这是一个父工程。

  3. (可选)删除生成的 src 目录,因为父工程中通常不包含业务代码。

<!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
<packaging>pom</packaging>
4.3.2 创建模块工程

在父工程中配置依赖的统一管理:使用 dependencyManagement 标签可以在父 POM 中定义依赖的版本,而不需要在子模块中重复定义。这样做的好处是,当需要更新依赖版本时,只需在父 POM 中修改一次即可,无需逐个修改子模块。

使用dependencyManagement标签配置对依赖的管理,如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mlz.team</groupId>
    <artifactId>maven-demo-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>demo-module</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>5.3.19</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>
4.3.3 子工程中引用依赖

在子模块的 pom.xml 文件中,通过 parent 标签指定当前工程的父工程。然后,在 dependencies 标签中引用父工程管理的依赖时,可以省略版本号。这样做可以确保子模块中的依赖版本与父工程保持一致。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <!-- 使用parent标签指定当前工程的父工程 -->
    <parent>
        <artifactId>maven-demo-parent</artifactId>
        <groupId>com.mlz.team</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <!-- 子工程的坐标 -->
    <!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
    <artifactId>demo-module</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>
    </dependencies>
    
</project>
4.3.4 修改父工程依赖信息的版本

随着技术的不断发展,依赖的版本也会不断更新。为了确保项目的稳定性和兼容性,我们需要定期检查和更新依赖的版本。在父 POM 中修改依赖版本时,可以遵循一定的策略,如优先使用稳定版本、避免频繁升级等。

<!-- 通过自定义属性,统一指定Spring的版本 -->
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- 自定义标签,维护Spring版本数据 -->
    <spring.version>5.3.19</spring.version>
</properties>
4.3.5 父工程中声明自定义属性

为了更方便地管理和更新依赖版本,我们可以在父 POM 中声明自定义属性。这些属性可以在整个项目中统一使用,从而实现一处修改、处处生效的效果。例如,我们可以声明一个 spring.version 属性来统一指定 Spring 框架的版本号。

  • 父 POM 示例 (pom.xml)

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>parent-pom</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <!-- 自定义属性 -->
    <properties>
        <java.version>11</java.version>
        <spring.version>5.3.10</spring.version>
        <junit.version>5.7.2</junit.version>
    </properties>

    <!-- 依赖管理 -->
    <dependencyManagement>
        <dependencies>
            <!-- Spring 依赖 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!-- JUnit 依赖 -->
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter-engine</artifactId>
                <version>${junit.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- 构建配置 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • 子项目 POM 示例 (child-pom.xml)

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>parent-pom</artifactId>
        <version>1.0.0</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>child-project</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <dependencies>
        <!-- 直接引用父 POM 中的依赖,无需指定版本号 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

4.4 指定 JDK 版本

把 Maven 工程部署都服务器上,脱离了 settings.xml 配置,如何保证程序正常运行呢?思路就是我们直接把 JDK 版本信息告诉负责编译操作的 maven-compiler-plugin 插件,让它在构建过程中,按照我们指定的信息工作。如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 项目基本信息,如 groupId, artifactId, version 等 -->
    <groupId>com.example</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 配置构建行为 -->
    <build>
        <plugins>
            <!-- 配置 Java 编译器插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <!-- 使用较新的版本,以确保兼容性和功能 -->
                <version>3.8.1</version> <!-- 或者更高版本 -->

                <configuration>
                    <!-- 指定源代码和目标代码的 JDK 版本 -->
                    <source>1.8</source>
                    <target>1.8</target>
                    <!-- 设置编译使用的字符编码 -->
                    <encoding>UTF-8</encoding>
                    <!-- 可选:显示详细的编译输出信息 -->
                    <verbose>true</verbose>
                    <!-- 可选:强制覆盖已存在的输出文件 -->
                    <forceJavacCompilerUse>true</forceJavacCompilerUse>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.5 SpringBoot 定制化打包

在 Maven 项目中,使用 spring-boot-maven-plugin 插件来定制化 Spring Boot 应用的打包过程是非常重要的。这个插件能够生成一个可执行的 jar 文件(通常称为 fat jar 或 uber jar),其中包含了应用所需的所有依赖项,以及 Spring Boot 的加载器,允许你使用 java -jar 命令直接启动应用。

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 项目基本信息,如 groupId, artifactId, version 等 -->
    <groupId>com.example</groupId>
    <artifactId>my-spring-boot-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging> <!-- 明确指定打包方式为 jar -->

    <!-- 父项目信息(可选,但通常用于继承 Spring Boot 依赖管理) -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <!-- 配置构建行为 -->
    <build>
        <plugins>
            <!-- 配置 Spring Boot Maven 插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!-- 如果使用了父 POM,这里通常不需要显式指定版本,因为父 POM 已经管理了版本 -->
                <!-- <version>2.5.5</version> --> <!-- 可选,如果未使用父 POM 则需要指定 -->

                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal> <!-- 默认目标,用于重新打包为可执行的 jar -->
                        </goals>
                    </execution>
                </executions>

                <!-- 可选配置,如需要可以添加 -->
                <!-- <configuration>
                    <mainClass>${start-class}</mainClass> <!-- 指定主类,如果 Spring Boot 未能自动检测到 -->
                    <classifier>exec</classifier> <!-- 可选,为生成的 jar 添加一个分类器 -->
                </configuration> -->
            </plugin>
        </plugins>
    </build>

    <!-- 项目依赖项 -->
    <dependencies>
        <!-- 添加你的 Spring Boot 依赖项 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 其他依赖项 -->
    </dependencies>

</project>

4.6 依赖配置补充

在 Maven 项目中,当需要管理多个不同体系的依赖,但又不能通过继承多个父 POM 时,使用 <dependencyManagement> 节点并设置 <scope>import</scope> 是一个很好的解决方案。这种方式允许你导入其他 POM 文件中的依赖管理信息,从而在你的项目中统一管理这些依赖的版本。

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- ... 其他项目配置 ... -->

    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud 依赖管理 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Spring Cloud Alibaba 依赖管理 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- 其他依赖管理,如需要可以添加 -->
            <!-- 例如:
            <dependency>
                <groupId>其他组织</groupId>
                <artifactId>其他依赖管理POM</artifactId>
                <version>版本号</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            -->
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- 在这里添加你的项目依赖项,不需要指定版本号,因为已经在 <dependencyManagement> 中管理了 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 其他依赖项 -->
    </dependencies>

    <!-- ... 其他项目配置,如 properties、build 等 ... -->

    <!-- 注意:确保在 properties 中定义了 ${spring-cloud.version} 和 ${spring-cloud-alibaba.version} 的值 -->
    <properties>
        <spring-cloud.version>2021.0.1</spring-cloud.version> <!-- 示例版本,实际使用时请替换为所需版本 -->
        <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version> <!-- 示例版本,实际使用时请替换为所需版本 -->
        <!-- 其他属性定义 -->
    </properties>

</project>

4.7 多环境管理

在开发过程中,我们的软件会面对不同的运行环境,比如开发环境、测试环境、生产环境,而我们的软件在不同的环境中,有的配置可能会不一样,比如数据源配置、日志文件配置、以及一些软件运行过程中的基本配置,那每次我们将软件部署到不同的环境时,都需要修改相应的配置文件,这样来回修改,很容易出错,而且浪费劳动力。

因此我们可以利用 Maven 的 profile 来进行定义多个 profile,然后每个profile对应不同的激活条件和配置信息,从而达到不同环境使用不同配置信息的效果。

4.7.1 准备 Spring Boot 项目

确保您的 Spring Boot 项目已经创建并配置好。

4.7.2 创建多环境配置文件

src/main/resources 目录下创建以下文件:

  • application.yml:包含所有环境的公共配置。

  • application-dev.yml:包含开发环境的特定配置。

  • application-test.yml:包含测试环境的特定配置。

  • application-prod.yml:包含生产环境的特定配置。

4.7.3 配置 application.yml

application.yml 中,您不需要设置 spring.profiles.active,而是可以添加一些通用的配置。例如:

server:
  port: 8080  # 公共配置,所有环境都使用8080端口(或根据需求修改)
# 其他公共配置...
4.7.4 配置环境特定文件

在每个环境特定的配置文件中(application-dev.yml, application-test.yml, application-prod.yml),添加该环境特有的配置。例如,在 application-prod.yml 中:

spring:
  datasource:
    url: jdbc:mysql://prod-db-server:3306/mydb
    username: prod-user
    password: prod-password
# 其他生产环境特有的配置...
4.7.5(可选)使用 Maven Profiles

虽然 Spring Boot 已经提供了多环境支持,但如果您想在构建时做一些环境特定的操作(如资源过滤、依赖管理等),可以配置 Maven profiles。在 pom.xml 中添加:

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <spring.profiles.active>dev</spring.profiles.active>
        </properties>
        <!-- 其他构建配置... -->
    </profile>
    <profile>
        <id>test</id>
        <properties>
            <spring.profiles.active>test</spring.profiles.active>
        </properties>
        <!-- 其他构建配置... -->
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <spring.profiles.active>prod</spring.profiles.active>
        </properties>
        <!-- 其他构建配置... -->
    </profile>
</profiles>

注意:通常不需要在 Maven 中设置 spring.profiles.active,因为 Spring Boot 提供了更好的方式来动态指定活动配置文件。这里的 Maven profiles 主要用于构建时的其他配置。

4.7.6 动态指定活动配置文件

在部署或运行应用时,使用以下方法之一来指定活动的配置文件:

  • 环境变量:设置 SPRING_PROFILES_ACTIVE 环境变量。例如,在 Linux 上:export SPRING_PROFILES_ACTIVE=prod

  • 命令行参数:在启动应用时添加 --spring.profiles.active={profile} 参数。例如:java -jar your-app.jar --spring.profiles.active=dev

  • 外部配置文件:使用 --spring.config.location 参数指定外部配置文件的路径。例如:java -jar your-app.jar --spring.config.location=/path/to/external/application-{profile}.yml

4.7.6 (可选)使用配置中心

对于大型项目或微服务架构,考虑使用配置中心(如 Spring Cloud Config)来集中管理配置。

4.8 jar 包冲突问题

在设定项目依赖时,由于初次设定可能存在问题,需要由专人负责调整依赖配置。为避免团队中每个程序员各自添加依赖导致的混乱和冲突,应统一管理依赖。初期需根据项目实际情况不断调整依赖,最终确定一个稳定的版本,并在父工程中统一管理。这样,即使开发中遇到问题,也只需修改父工程的依赖管理配置,而无需改动每个模块。解决依赖冲突的基本思路是找到冲突的jar包,并在其中选定一个版本,通过exclusions排除其他版本或明确声明所需版本。

4.8.1 使用 IDEA 的 Maven Helper 插件
  1. 安装插件:

    1. 在 IntelliJ IDEA 中,打开 Settings(或 Preferences 在 macOS 上)。

    2. 导航到 Plugins,搜索 Maven Helper 并安装它。

  2. 使用插件查找冲突:

    1. 打开项目的 pom.xml 文件。

    2. 右键点击 pom.xml 文件,选择 Maven -> Show Dependencies

    3. 在弹出的窗口中,选择 Conflicts 标签页。

    4. 这里将列出所有冲突的 JAR 包及其不同版本和来源。

  3. 解决冲突:

    1. 根据冲突信息,决定使用哪个版本的 JAR 包。

    2. pom.xml 文件中,使用 <exclusions> 标签排除不需要的版本。

    3. 或者,使用 <dependencyManagement> 在父 POM 中统一管理依赖版本。

4.8.2 使用 Maven 的 enforcer 插件

Maven Enforcer 插件可以帮助检测项目中的潜在问题,包括依赖冲突和类路径中的重复类。

  1. 配置 enforcer 插件:

    1. 在项目的 pom.xml 文件中,添加 enforcer 插件的配置

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-enforcer-plugin</artifactId>
            <version>3.0.0-M3</version> <!-- 使用最新版本 -->
            <executions>
                <execution>
                    <id>enforce-rules</id>
                    <goals>
                        <goal>enforce</goal>
                    </goals>
                    <configuration>
                        <rules>
                            <!-- 检测依赖冲突 -->
                            <dependencyConvergence/>
                            <!-- 检测类路径中的重复类(可选) -->
                            <banDuplicateClasses/>
                        </rules>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

注意:banDuplicateClasses 规则可能需要额外的配置来指定哪些包或类应该被检查。此外,这个规则可能会比较耗时,因为它需要扫描整个类路径。

  1. 运行 enforcer 插件:

    1. 在命令行中,运行 mvn enforcer:enforce 来执行插件。

    2. 如果检测到问题,插件将输出错误信息,包括冲突的依赖或重复的类。

5 Maven 流水线配置

Maven是Java后端项目常用的构建依赖管理工具,它通过pom.xml文件定义项目的依赖包信息和构建配置。

5.1 安装配置Maven环境

5.1.1 下载Maven安装包

从Maven官方源Download Apache Maven – Maven下载最新版本的Maven安装包。例如,使用wget命令下载Maven 3.8.6版本(注意:原示例中的版本号存在笔误,已更正):

wget https://mirrors.bfsu.edu.cn/apache/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz

5.1.2 解压安装包

将下载的安装包解压到指定目录,例如/usr/local/

tar zxf apache-maven-3.8.6-bin.tar.gz -C /usr/local/
cd /usr/local/
ln -s apache-maven-3.8.6 maven  # 创建一个符号链接,方便后续操作
5.1.3 配置环境变量

编辑/etc/profile文件,添加Maven的环境变量:

vi /etc/profile
export M2_HOME=/usr/local/maven
export PATH=$M2_HOME/bin:$PATH
source /etc/profilemvn -v

注意:这里使用了符号链接maven,因此M2_HOME设置为/usr/local/maven

5.1.4 验证安装

使用mvn -v命令验证Maven是否安装成功,并检查Java版本和Maven版本是否匹配:

mvn -v

输出应类似于:

Apache Maven 3.8.6 (your-maven-home)
Maven home: /usr/local/maven
Java version: 1.8.0_xxx, vendor: Oracle Corporation, runtime: /path/to/jdk
Default locale: zh_CN, platform encoding: UTF-8
OS name: "linux", version: "your-os-version", arch: "amd64", family: "unix"

5.2 Jenkins集成

在Jenkins流水线中集成Maven进行项目构建并不复杂,只需在Pipeline脚本中执行Maven命令即可。

5.2.1 Jenkins Pipeline配置

在Jenkins的Pipeline配置中,添加一个构建阶段(Build Stage),并在该阶段中执行Maven命令。例如:

//Jenkins Pipeline中的Build阶段
stage("Build"){
steps{
        script{
            //sh执行Shell命令
            sh "mvn clean package"
        }
    }
}
5.2.2 注意事项
  1. 确保Jenkins服务器已经安装了Maven,并且Maven的路径已经配置在Jenkins的全局工具配置中(虽然直接在Pipeline脚本中指定路径也可以)。

  2. 如果项目有特定的Maven设置(如settings.xml),可以在Pipeline脚本中指定--settings参数来覆盖默认设置。

  3. 根据项目的需要,可以在mvn命令后添加其他参数,如-DskipTests=true来跳过测试等。

二、Gradle构建:灵活性与扩展性的典范

Gradle,作为一款开源的自动化构建工具,凭借其强大的多语言支持能力,以及对Apache Ant和Maven的精髓融合,已经在构建工具领域崭露头角。与Ant的随意性和Maven的繁琐配置及严格生命周期相比,Gradle以其规范性和更高的灵活性,为开发者带来了全新的构建体验。

作为开发者,我们深知项目自动构建在日常工作中的重要性。想象一下,如果构建代码能像源代码一样易于扩展、测试和维护,那将极大地提升我们的工作效率。Gradle正是为此而生,它采用DSL(领域特定语言,如Groovy)来编写构建脚本,使得脚本更加简洁明了,易于理解和维护。

Gradle的构建脚本是声明式的,能够清晰地表达构建意图。与XML相比,使用Groovy编写构建脚本大大减少了代码量,提高了可读性。更重要的是,Gradle还集成了其他构建工具,如Ant和Maven,使得原有项目能够轻松迁移到Gradle平台,无需进行大规模的改造。

  • Gradle的优势:

  1. 灵活性强:Gradle提供丰富的API,允许用户根据需求定制项目的构建过程。相较于Maven和Ant,Gradle的构建脚本更加灵活且易于编写。

  2. 粒度精细:在Gradle中,源码编译、资源编译等任务都是通过Task来完成的。用户可以自由修改Task,实现对构建过程的精细控制。

  3. 扩展性高:Gradle支持插件机制,用户只需简单配置即可复用这些插件,从而轻松扩展Gradle的功能和适用范围。

  4. 兼容性好:Gradle不仅功能强大,还能完美兼容Maven和Ant的功能。这意味着用户可以在Gradle中继续使用他们熟悉的Maven和Ant构建脚本和插件。

  5. 趋势引领:众多大厂的开源代码已经开始转向Gradle管理,这已成为构建工具领域的一大趋势。

  • Gradle的劣势:

  1. 版本兼容性:虽然Gradle的每个版本都可能带来较大的改动,但Gradle团队正在逐步改善这一问题。同时,使用Gradle Wrapper可以确保团队成员使用相同版本的Gradle,减少因版本差异带来的问题。

  2. 学习成本:对于不熟悉Groovy的用户来说,学习成本可能较高。但Gradle也提供了Kotlin DSL作为替代方案,降低了学习难度。此外,Gradle社区和文档资源也非常丰富,有助于用户快速上手。

  3. 踩坑成本:由于Gradle的灵活性和复杂性,用户在使用过程中可能会遇到一些挑战。但Gradle社区和官方支持都非常活跃,用户可以通过社区论坛、官方文档和培训课程等途径寻求帮助,降低踩坑成本。

1 Gradle组成

Gradle是一个功能强大的开源构建自动化工具,它结合了Apache Ant的灵活性和Apache Maven的依赖管理优势,并在此基础上进行了创新。Gradle的组成主要包括以下几个方面:

1.1 Groovy(或其他DSL)

Groovy是一种基于JVM(Java虚拟机)的动态语言,它提供了与Java相似的语法,但更加灵活和动态。Groovy完全兼容Java,并在此基础上增加了闭包、DSL(领域特定语言)支持等特性,使得编写构建脚本变得更加简洁和高效。虽然Gradle也支持使用Kotlin DSL等其他DSL来编写构建脚本,但Groovy仍然是Gradle最原始和最常用的DSL。

对于想要执行Groovy脚本的用户,他们需要安装Groovy环境或使用Java的ClassLoader来调用。然而,对于初学者来说,寻找在线的云编译环境来运行Groovy脚本可能是一个更便捷的选择。

1.2 构建脚本块

构建脚本块是Gradle构建过程的核心组成部分,它们由Groovy(或其他DSL)语法组成。这些脚本块定义了项目的构建过程、依赖关系、任务等。Gradle内置了一些闭包(如builddependencies等)和插件提供的闭包(如warjava等),用户可以在这些闭包中编写自定义的构建逻辑。

1.3 Gradle API

Gradle API为用户提供了一系列内置属性和方法,用于在构建脚本中执行各种操作。这些内置属性和方法包括:

  • 内置属性:如project(表示当前项目对象)、version(表示项目的版本号)、group(表示项目的组ID)、parent(表示父项目对象,如果存在的话)等,用于描述项目的基本信息。

  • 内置方法:如property(用于定义项目属性)、copy(用于复制文件和目录)、apply(用于应用其他脚本或插件)、afterEvaluate(用于在项目评估后执行某些操作)等,用于执行各种构建任务和操作。

2 Gradle安装

由于Gradle不同版本之间的兼容性较差,为了确保项目的稳定性和可维护性,用户通常不会直接下载安装Gradle的某个特定版本。相反,他们更倾向于采用Gradle Wrapper这种更加灵活和可维护的方式来管理Gradle版本。

2.1 Gradle Wrapper

Gradle Wrapper是一个允许用户在不手动安装Gradle的情况下运行Gradle构建的工具。它包含了一个特定版本的Gradle二进制文件和一个脚本,用于下载和配置正确的Gradle版本。这样,无论用户在哪个环境中工作,都可以确保他们使用的是与项目兼容的Gradle版本。

要使用Gradle Wrapper,用户只需在项目中包含gradlew(Unix/Linux/Mac)或gradlew.bat(Windows)脚本,以及gradle/wrapper/gradle-wrapper.properties文件。这些文件定义了要使用的Gradle版本和其他相关配置。然后,用户就可以通过运行这些脚本来执行Gradle构建任务了。

2.2 集成开发环境(IDE)支持

许多流行的集成开发环境(如IntelliJ IDEA、Eclipse等)都提供了对Gradle的内置支持。这些IDE通常允许用户通过简单的配置来指定Gradle Wrapper的路径,从而自动使用正确的Gradle版本来执行构建任务。此外,这些IDE还提供了丰富的Gradle插件和工具,用于简化构建过程、调试代码、管理依赖关系等。因此,在使用Gradle时,充分利用IDE的支持可以大大提高开发效率和构建过程的可靠性。

3 Gradle项目

Gradle项目结构清晰,功能强大,其核心在于build.gradle文件,该文件定义了项目的构建逻辑、依赖关系及插件等。以下是对Gradle项目结构的深入剖析及build.gradle文件的详细解读。

3.1 项目结构解析

├─build.gradle                        ① 构建脚本核心文件
├─gradlew                             ② Linux/Unix/Mac下的Gradle Wrapper脚本
├─gradlew.bat                         ③ Windows下的Gradle Wrapper脚本
├─settings.gradle                     ④ 项目全局设置文件
├─gradle                              ⑤ Gradle Wrapper目录
│  └─wrapper                          
│      ├─ gradle-wrapper.jar          
│      ├─ gradle-wrapper.properties   
└─src                                 ⑥ 源代码目录
    ├─main                            主代码目录
    └─test                            测试代码目录
  1. build.gradle(①):

    1. 这是Gradle构建脚本的核心文件,用于定义项目的构建逻辑、依赖关系、插件等。

    2. 全局build.gradle文件通常位于项目根目录,用于设置全局性的配置,如仓库源和Gradle版本号。

    3. 模块内的build.gradle文件则负责该模块的特定构建配置。

  2. gradlew(②):

    1. 这是Linux/Unix/Mac系统下的Gradle Wrapper脚本,允许用户在不手动安装Gradle的情况下执行Gradle构建任务。

    2. 通过运行./gradlew命令,用户可以轻松触发构建过程。

  3. gradlew.bat(③):

    1. 这是Windows系统下的Gradle Wrapper脚本,功能与gradlew类似,但专为Windows环境设计。

    2. 用户只需双击gradlew.bat或在命令行中执行该脚本,即可启动Gradle构建。

  4. settings.gradle(④):

    1. 此文件用于定义项目的全局设置,如项目名称、包含的模块等。

    2. 无论项目包含多少个子模块,settings.gradle文件都只有一个,且必须位于根项目目录中。

  5. gradle/wrapper(⑤):

    1. 此目录包含Gradle Wrapper所需的两个关键文件:gradle-wrapper.jargradle-wrapper.properties

    2. gradle-wrapper.jar负责实际的Wrapper逻辑,而gradle-wrapper.properties则存储了Wrapper的配置信息,如Gradle版本等。

    3. 通过Wrapper,Gradle可以自动下载并安装指定版本的Gradle环境,从而简化版本管理。

  6. src(⑥):

    1. 这是源代码的存放目录,通常包含main(主代码)和test(测试代码)两个子目录。

    2. main目录用于存放项目的核心代码和资源文件,而test目录则用于存放单元测试代码。

3.2 build.gradle文件详解

build.gradle 文件是 Gradle 构建系统中的一个核心配置文件,用于定义项目的构建脚本。Gradle 是一个开源的自动化构建工具,它允许你以声明性的方式定义项目的构建逻辑,包括依赖管理、任务执行等。build.gradle 文件通常包含以下内容:

  1. 插件应用:通过 plugins 块应用 Gradle 插件,这些插件为 Gradle 添加了额外的功能,如 Java 编译、测试运行、打包等。

  2. 项目信息:定义项目的组名(group)、版本号(version)、Java 版本兼容性(sourceCompatibilitytargetCompatibility)等。

  3. 仓库配置:指定 Gradle 查找依赖项的仓库地址,如 Maven 中央仓库、私有仓库等。

  4. 依赖管理:在 dependencies 块中声明项目所需的依赖项,Gradle 会自动从配置的仓库中下载这些依赖项。

  5. 任务配置:定义和配置 Gradle 任务,这些任务可以是自定义的,也可以是插件提供的。

  6. 其他配置:如构建脚本依赖(buildscript 块)、发布配置等。

// 插件声明
plugins {
    id 'org.springframework.boot' version '2.7.4'
    id 'io.spring.dependency-management' version '1.0.14.RELEASE'
    id 'java'
    id 'war' // 如果您需要构建 WAR 文件
}

// 项目坐标信息
group = 'com.mlz'
version = '1.0.0'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'

// 仓库地址
repositories {
    // 推荐使用阿里云的镜像仓库,加速依赖下载
    maven { url 'https://maven.aliyun.com/repository/public/' }
    maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
    mavenCentral()
}

// 第三方依赖
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // 其他依赖可以在这里添加
}

// 单元测试声明(已经在 Spring Boot 插件中默认配置,这里可以省略)
// tasks.named('test') {
//     useJUnitPlatform()
// }

// buildscript 区域用于配置 Gradle 自身所需的依赖和仓库
buildscript {
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public/' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
    }
    dependencies {
        // 其他 Gradle 构建脚本所需的依赖可以在这里添加
    }
}

// 注意:allprojects 和 subprojects 区域通常用于多模块项目
// 如果您的项目是单模块项目,这些区域可以省略
// allprojects 和 subprojects 的配置应该放在根项目的 build.gradle 文件中(如果有的话)

// 自定义任务
task cleanx {
    doLast {
        println "Hello Gradle"
    }
}

// 注意:如果您不需要发布到 Bintray,可以移除相关的插件和配置
// 如果您需要使用 Docker 插件,请确保选择了正确的插件并配置了相应的依赖和仓库

3.3 Project对象的组成

在Gradle中,每个build.gradle文件都代表一个Project实例的配置。Project对象具有丰富的属性和方法,用于定义项目的构建逻辑。

3.3.1 属性
  1. 内置属性:

    1. 可以直接赋值,无需声明。

    2. 例如:group = 'com.mlz'version = '1.0.0'

  2. 自定义属性:

    1. 可以使用Groovy语法或Java语法结合。

    2. Groovy定义属性:def pname = "projectName:${project.name}"

    3. Java类型接收:String pname = "projectName:${project.name}"

  3. 扩展属性:

    1. 使用ext命名空间来扩展属性,定义后可以在project、task、subproject中读取和更新。

    2. 例如:ext.prop1 = 'hjw'ext.prop2 = 'gradle'

  4. 属性作用域:

    1. Project对象自身:包含所有通过getters和setters方法定义的属性。

    2. ext属性(extra):一个包含任意名称-值对的映射。

    3. 插件扩展属性(extensions):每个扩展名都可以作为只读属性使用。

    4. 插件约定属性(convention):通过Convention对象添加到Project中的属性。

    5. Tasks:使用任务名称作为属性名称来访问任务,只读。

    6. 继承属性:ext的属性和约定属性从项目的父级继承,递归到根项目,只读。

  5. 常用project属性:

    1. allprojects:包含此项目及其子项目的集合。

    2. buildDir:当前项目的编译目录,默认值为projectDir/build

    3. defaultTasks:当前项目的默认任务集合。

    4. group:当前项目的组名。

    5. logger:当前项目的日志器。

    6. name:此项目的名称。

    7. parent:此项目的父项目。

    8. path:此项目的绝对路径。

    9. project:当前project对象实例。

    10. rootDir:本项目的根目录。

    11. rootProject:当前项目层次结构中的根project。

    12. subprojects:当前项目的子项目集合。

    13. tasks:本项目的task集合。

    14. version:此项目的版本。

3.3.2 方法
  1. 方法作用域:

    1. Project对象自身的方法。

    2. build.gradle脚本文件中定义的方法。

    3. 通过插件添加到Project中的扩展方法。

    4. 插件添加到项目中的约定方法。

    5. 项目中的Tasks,每个Task都会添加一个方法,方法名是任务名。

  2. 常用Project方法:

方法描述
afterEvaluate可以添加一个闭包,它会在项目完成评估后立即执行。当执行属于该项目的构建文件时,会通知此类监听器。
allprojects配置当前项目以及它的每个子项目
apply应用零个或多个插件或脚本。
beforeEvaluate添加一个闭包,它会在项目开始评估前立即执行
configure通过闭包配置对象集合。
copy复制指定的文件
defaultTasks设置此项目的默认任务的名称。当开始构建时没有提供任务名称时使用这些。
delete删除文件和目录
exec执行外部命令
file解析相对于该项目的项目目录的文件路径
findProject按路径定位项目。如果路径是相对的,则相对于该项目进行解释。
findProperty找特定属性,如果未找到,则返回给定属性的值或 null
getAllTasks返回此项目中包含的任务的地图
hasProperty确定此项目是否具有给定的属性
javaexec执行 Java 主类
javaexec执行外部 Java 进程。
mkdir创建一个目录并返回一个指向它的文件。
property返回给定属性的值。此方法定位属性如下:
setProperty设置此项目的属性。此方法在以下位置搜索具有给定名称的属性,并将该属性设置在它找到该属性的第一个位置。
subprojects配置本项目的子项目
task创建Task具有给定名称的 a 并将其添加到此项目中
uri将文件路径解析为 URI,相对于该项目的项目目录
3.3.3 插件

获取插件的渠道主要有两种:

  • 访问Gradle插件官网

  • Github搜索,有一些插件并没有被官方所收录,但仍然能够使用,但需要承担一些隐藏的风险

当前使用频率较高的几款插件包括:SpringBoot构建插件、Docker容器集成插件、JUnit单元测试插件等。

(1)脚本插件script plugins

脚本插件通常是一个脚本文件,与普通的build.gradle文件无异。它其实并非一个真正的插件,而是一个扩展脚本,但作用不容小觑,是脚本模块化的基础。我们可以将复杂的脚本文件拆分成多个职责明确的脚本插件,就像封装Utils工具类一样。脚本可以存放在本地或网络上,引用方式如下:

  • 使用本地插件

apply from: './other.gradle'
apply from: this.rootProject.file('other.gradle')
  • 使用网络远程插件

apply from: 'https://gitee.com/xxx/xx/raw/master/it235.gradle'
(2)二进制插件binary plugins

二进制插件通过插件ID来应用,ID是插件的全局唯一标识符或名字。Gradle中的插件按来源分为核心插件和非核心插件。核心插件有简短ID,如Java插件的ID为java。非核心二进制插件必须使用完全限定形式的ID(如com.github.foo.bar)。使用二进制插件有两种方式:

  1. 结合buildscript{}应用插件(老版本)

//build.gradle中的顶级语句,如下分别是使用java/idea/war/docker插件
apply plugin: 'java'//apply plugin: JavaPlugin 也可以通过指定插件类来应用,与java效果一样
apply plugin: 'idea'
apply plugin: "war"//声明
apply plugin: "com.jfrog.bintray"
apply plugin: 'org.akhikhl.gretty'//buildscript
buildscript {
  repositories {
    maven {url "https://maven.aliyun.com/repository/public"}
    maven { url 'https://maven.aliyun.com/repository/jcenter' }
  }//应用插件
  dependencies {
    classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0"
    classpath 'org.akhikhl.gretty:gretty:+'
  }
}
  1. 使用plugins{} DSL新写法,对应的是PluginDependenciesSpec实例

//build.gradle中的顶级语句,声明和应用放在一起
plugins {//核心插件,gradle提供
  id 'java'
  id 'eclipse'
  id 'war'//非核心插件(社区插件),必须通过id+version的全限定名的方式进行引用
  id 'com.bmuschko.docker-remote-api' version '6.7.0'//apply false 表示在父项目中声明,但是父项目不会立即加载,可以在子项目中通过ID的方式进行使用
  id 'com.jfrog.bintray' version '1.8.5' apply false
}
//注意:plugins暂时不支持第三方插件,如果要使用第三方插件请使用老的写法。同时plugins中不能随意编写其他的语句体
(3)自定义插件

自定义插件包括三个步骤:建立插件工程、配置参数、发布插件与使用插件。插件的源码可以存放在以下三个地方:

  • 在构建脚本中

class GreetingPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.task('hello') {
      doLast {
        println 'Hello from the GreetingPlugin'
      }
    }
  }
}

apply plugin: GreetingPlugin

使用gradle -q hello即可执行。这种方式的好处是当前项目能自动编译和加载插件,但插件在构建脚本之外不可见。

  • buildSrc项目中

将插件源码放在rootProjectDir/buildSrc/src/main/javarootProjectDir/buildSrc/src/main/groovyrootProjectDir/buildSrc/src/main/kotlin目录下。Gradle会负责编译和测试插件,并使其在构建脚本的类路径中可用。该插件对构建使用的每个构建脚本都可见,但在构建之外不可见。

  • 在独立项目中

创建一个单独的项目,生成并发布一个JAR文件,然后可在多个项目中使用该插件,其他开发者也能下载使用。这种方式通常用于编写或依赖一些插件,或将几个相关的任务类捆绑到一起。

4 Gradle任务

在Gradle中,Task是构建脚本的基本执行单元。Gradle提供了多种方式来定义和管理Task,以满足不同构建需求。以下是Gradle Task的详细解析和用法示例。

4.1 Task的定义

4.1.1 直接声明
task taskOne {
    println "Executing taskOne"
}
4.1.2 使用字符串参数定义名称
task('taskTwo') {
    println "Executing taskTwo"
}
4.1.3 使用TaskContainer的create方法
tasks.create('taskThree') {
    println "Executing taskThree"
}
4.1.4 使用register方法(延迟创建)
tasks.register('taskSix') {
    println "Executing taskSix"
}

注意:register方法创建的Task是延迟创建的,只有当Task被实际需要执行时才会被创建。

4.2 TaskContainer解析

tasks实际上是TaskContainer的实例对象的映射属性,TaskContainer实现了TaskCollectionPolymorphicDomainObjectContainer,因此tasks具有这两个类的所有行为。

  • 定位Task

    • findByPath:如果没找到会返回null。

println tasks.findByPath('say')?.path
    • getByPath:如果没找到会抛出UnknownTaskException

println tasks.getByPath('say').path
  • 创建Task

    • 直接创建:使用create

    • 延迟创建:使用register

  • 替换Task 使用replace方法创建一个新的Task,并替换同名的旧Task。

4.3 Task的配置

  • 设置Group和Description

task myTask {
    group = 'it235'
    description = 'My own task'
}
  • doFirst和doLast doFirst用于添加需要最先执行的Action,doLast用于添加需要最后执行的Action。

task myTask {
    doFirst {
        println 'This is executed first'
    }
    doLast {
        println 'This is executed last'
    }
}

4.4 带参Task和指定Type

  • 带参Task

task paramTask(group: 'it235', description: 'My parameterized task', dependsOn: ['myTask1', 'myTask2']) {
    doLast {
        println 'Executing paramTask'
    }
}
  • 指定Type 通过type指定Task的基类,如CopyDelete等。

task copyTask(type: Copy) {
    from 'src/main/resources'
    into 'build/resources/main'
}

4.5 Task依赖与排序

  • Task依赖

task hello {
    doLast { println 'Hello!' }
}

task intro {
    dependsOn hello
    doLast { println 'I\'m Junge.' }
}
  • Task排序

    • mustRunAfter:表示必须遵守的顺序关系。

    • shouldRunAfter:表示不强制的顺序关系,可以在某些情况下被忽略。

task taskX {
    doLast { println 'www.it235.com' }
}

task taskY {
    doLast { println 'hello' }
}

taskY.mustRunAfter taskX
// 或者 taskY.shouldRunAfter taskX

4.6 条件执行与超时

  • 条件执行 使用onlyIf方法根据条件判断是否执行Task。

task taskX {
    doLast { println 'www.it235.com' }
}

task taskY {
    doLast { println 'hello' }
}

taskY.mustRunAfter taskX
// 或者 taskY.shouldRunAfter taskX
  • 超时设置 使用timeout属性设置Task的超时时间。

import java.time.Duration

task hangingTask {
    doLast {
        Thread.sleep(100000)
    }
    timeout = Duration.ofMillis(500)
}

4.7 Task规则与Finalizer

  • ask规则 使用tasks.addRule方法定义Task规则。

tasks.addRule("Pattern: ping<ID>") { String taskName ->
    if (taskName.startsWith("ping")) {
        task(taskName) {
            doLast { println "Pinging: " + (taskName - 'ping') }
        }
    }
}
  • Finalizer Task 使用finalizedBy方法指定Finalizer Task,该Task会在主Task执行完毕后执行,即使主Task抛出异常也会执行。

task execute {
    doLast {
        println 'Executing main task'
    }
}

task release {
    doLast {
        println 'Releasing resources'
    }
}

execute.finalizedBy release

5 Gradle生命周期

在Gradle的生命周期中,每个阶段都承担着特定的任务,并且Gradle提供了钩子函数(hook),允许开发者在这些阶段插入自定义逻辑。以下是对Gradle生命周期的详细剖析:

4.1 初始化阶段

  任务:解析settings.gradle文件,确定参与构建的项目,并创建Project实例。

  具体流程:

  1. 创建Settings实例:Gradle首先读取并解析settings.gradle文件,创建一个Settings实例。这个实例定义了项目的全局设置,例如包含哪些子项目。

  2. 解析并加载Project:根据Settings实例的配置,Gradle会扫描并解析settings.gradle中指定的项目。为每个项目创建对应的Project实例,这些项目的加载顺序是按照配置的层次结构进行的,确保父项目在子项目之前被加载。

  3. 初始化Project实例:值得注意的是,虽然此阶段会涉及build.gradle文件,但初始化Project实例时并不会深入解析build.gradle文件中的具体配置。这一步主要是为项目创建基本的框架和实例。

4.2 配置阶段

  任务:解析所有project中的build.gradle文件,获取所有的task,构建任务依赖图。

  具体流程:

  1. 执行build.gradle脚本:Gradle会循环执行每个项目的build.gradle脚本文件。这个过程中,Gradle会解析脚本中的配置,注册任务(Task),并确定任务之间的依赖关系。

  2. 构建任务依赖图:在解析完所有的build.gradle文件后,Gradle会根据任务之间的依赖关系构建一个任务依赖图(Task Dependency Graph)。这个图是一个有向无环图(DAG),表示了任务之间的执行顺序和依赖关系。

  3. 任务配置:在配置阶段,Gradle还会对每个任务进行初始化配置,包括设置任务的输入、输出和动作等。

4.3 执行阶段

  任务:按照任务依赖图执行所有指定的task。

  具体流程:

  1. 任务调度:Gradle会根据任务依赖图确定需要执行的任务及其执行顺序。如果某个任务的输入是另一个任务的输出,则该任务必须在另一个任务之后执行。

  2. 任务执行:Gradle会按照确定的顺序执行任务。在执行每个任务之前,Gradle会检查该任务的输入是否发生了变化。如果输入没有变化,并且任务的结果仍然是最新的,则Gradle会跳过该任务的执行,以提高构建效率。

  3. 任务完成:当所有任务都执行完毕后,Gradle会触发相应的钩子函数,表示构建过程已经完成。

  1.   

6 Gradle项目实践

在大型企业级项目中,代码复杂性和模块化需求显著增加。Gradle作为一个强大的构建工具,提供了多项目构建(multi-project build)功能,允许开发者将复杂的项目拆分为多个独立但相关的子项目,从而提高代码的可维护性和可扩展性。

6.1 项目模块化

模块化是提高代码可维护性和可扩展性的关键。通过模块化,可以将代码组织成独立的、易于管理的单元。

高内聚低耦合是模块化设计的基本原则。一个优秀的模块化示例是Spring框架,它提供了多种服务,如MVC Web框架、事务管理器、JDBC数据库连接等。架构师可以借鉴Spring框架的设计思路,将项目划分为不同的模块。

6.2 配置项目文件

在Gradle中,多项目构建(multi-project build)是一种强大的特性,允许你将多个相关的项目组织在一起,共享配置和依赖。settings.gradle 文件是定义这种项目层次结构的关键。

6.2.1 settings.gradle 文件的作用

settings.gradle 文件用于定义Gradle项目的层次结构。通过 include 方法,可以包含多个子项目,这些子项目可以是独立的模块,如模型(model)、仓库(repository)和Web应用(web)。

示例:

// 包含多个子项目
include 'model', 'repository', 'web'

这里的参数是子项目的名称(相对于根项目的目录名),而不是文件路径。例如,如果你的项目结构如下:

root/
├── build.gradle
├── settings.gradle
├── model/
│   └── build.gradle
├── repository/
│   └── build.gradle
└── web/
    └── build.gradle

那么,你只需在 settings.gradle 中使用 include 'model', include 'repository', 和 include 'web' 来包含这些子项目。

6.2.2 共享配置

在大型 Java 项目中,子项目之间必然具有相同的配置项。我们在编写代码时,要追求代码重用和代码整洁;而在编写 Gradle 脚本时,同样需要保持代码重用和代码整洁。Gradle 提供了不同的方式使不同的项目能够共享配置。

  • allprojects:allprojects 是父 Project 的一个属性,该属性会返回该 Project 对象以及其所有子项目。在父项目的build.gradle脚本里,可以通过给allprojects传一个包含配置信息的闭包, 来配置所有项目(包括父项目)的共同设置。通常可以在这里配置 IDE 的插件,group 和version 等信息,比如:

allprojects {
    apply plugin: 'idea'
}

这样就会给所有的项目(包括当前项目以及其子项目)应用上 idea 插件。

  • subprojects:subprojects 和 allprojects 一样,也是父 Project 的一个属性,该属性会返回所有子项目。在父项目的 build.gradle 脚本里,给 subprojects 传一个包含配置信息的闭包,可以配置所有子项目共有的设置,比如共同的插件、repositories、依赖版本以及依赖配置:

subprojects {

        apply plugin: 'java'
        repositories {
              mavenCentral()
        }
        ext {
                guavaVersion = ’14.0 .1’
                junitVersion = ‘4.10’
        }
        dependencies {
                compile(
                        “com.google.guava: guava: $ {
                                guavaVersion
                        }”
                )
           testCompile(

                        “junit: junit: $ {
                                junitVersion
                        }”
                )
        }
}

这就会给所有子项目设置上 java 的插件、使用 mavenCentral 作为 所有子项目的 repository 以及对 Guava[4]和 JUnit 的项目依赖。此外,这里还在 ext 里配置依赖包的版本,方便以后升级依赖的版本。

  • configure:在项目中,并不是所有的子项目都会具有相同的配置,但是会有部分子项目具有相同的配置,比如在我所在的项目里除了 cis-war 和 admin-war 是 web 项目之外,其他子项目都不是。所以需要给这两个子项目添加 war 插件。Gradle 的 configure 可以传入子项目数组,并为这些子项目设置相关配置。在我的项目中使用如下的配置:

configure(subprojects.findAll {it.name.contains('war')}) 
{ 
        apply plugin: 'war'
}

configure 需要传入一个 Project 对象的数组,通过查找所有项目名包含 war 的子项目,并为其设置war 插件。

6.2.3 独享配置

在项目中,除了设置共同配置之外, 每个子项目还会有其独有的配置。比如每个子项目具有不同的依赖以及每个子项目特殊的 task 等。

在父项目的 build.gradle 文件中通过 project(‘:sub-project-name’)来设置对应的子项目的配置。比如在子项目 model需要 Hibernate 的依赖,可以在父项目的 build.gradle 文件中添加如下的配置:

project(‘: model’) {
        ext {
                hibernateVersion = ‘4.2 .1.Final’
        }
        dependencies {
                compile“ org.hibernate: hibernate - core: $ {
                        hibernateVersion
                }”
        }

}

注意:这里子项目名字前面有一个冒号(:)。 通过这种方式,指定对应的子项目,并对其进行配置。

6.2.4 其他共享

在 Gradle 中,除了上面提到的配置信息共享,还可以共享方法以及 Task。可以在根目录的build.gradle 文件中添加所有子项目都需要的方法,在子项目的 build.gradle 文件中调用在父项目build.gradle 脚本里定义的方法。例如我定义了这样一个方法,它可以从命令行中获取属性,若没有提供该属性,则使用默认值:

def defaultProperty(propertyName, defaultValue) {

        return hasProperty(propertyName) ? project[propertyName] : defaultValue

}

注意,这段脚本完全就是一段Groovy 代码,具有非常好的可读性。

由于在父项目中定义了 defaultProperty 方法,因而在子项目的 build.gradle 文件中,也可以调用该方法。

6.2.5 设置文件的查找

Gradle允许从根目录或任何子目录中运行构建任务。为了确定一个子项目是否属于多项目构建的一部分,Gradle会按照以下步骤查找 settings.gradle 文件:

  1. 查找与当前目录具有相同嵌套级别的名为 settings.gradle 的文件,但位于一个名为 master 的目录中(这个行为可能因Gradle版本而异)。

  2. 如果在第一步中没有找到 settings.gradle 文件,Gradle会从当前目录开始逐步向上查找父目录,直到找到 settings.gradle 文件或到达根目录为止。

6.3 配置子项目

6.3.1 定义项目特有的行为

上面介绍过在父文件中可以配置字项目的独有配置,但配置简单的小型项目比较实用,若是对于子项目多,并且配置复杂的大型项目,推荐将各个项目的配置分别放到单独的 build.gradle 文件中去,可以方便设置和管理每个子项目的配置信息。

ext {
        hibernateVersion = ‘4.2 .1.Final’
}

dependencies {
        compile“ org.hibernate: hibernate - core: $ {
                hibernateVersion
        }”

}
6.3.2 环境的配置

Gradle 为不同环境提供配置信息的方法主要分为使用 Properties 文件和使用 Groovy 配置文件两种。

6.3.2.1 配置文件的加载

要为不同的环境提供不一样的配置信息,Maven 选择使用 profile,而 Gradle 则提供了两种方法为构建脚本提供Properties 配置:

1. 使用 Properties 文件

  • 文件结构:在项目的根目录下创建 config 文件夹,并在其中放置 development.propertiestest.propertiesproduction.properties 等不同环境的配置文件。

  • 加载逻辑:在 build.gradle 文件中,通过 -Pprofile=xxx 传递参数来指定要加载的配置文件。使用 ext 块定义 profile 变量,并根据该变量加载对应的 properties 文件。

  • 示例代码:

ext {
    profile = project.hasProperty('profile') ? project['profile'] : 'development' // 默认开发环境
}

def loadProperties() {
    def props = new Properties()
    new File("${rootProject.projectDir}/config/${profile}.properties").withInputStream { stream ->
        props.load(stream)
    }
    return props
}

def jdbcConfig = loadProperties()

2. 使用 Groovy 配置文件

  • 文件结构:在项目的根目录下创建 config.groovy 文件,并在其中定义不同环境的配置信息。

  • 加载逻辑:使用 ConfigSlurper 类根据 -Pprofile=xxx 传递的参数加载对应的配置信息。

  • 示例代码:

environments {
        development {
            jdbc {
                    url = 'development'
                    user = 'xxxx'
            }
        }
        test {
                jdbc {
                    url = 'test'
                    user = 'xxxx'
                    password = 'xxxx'
                }

        }
        production {
            jdbc {
                    url = 'production'
                    user = 'xxxx'
                    password = 'xxxx'
            }
        }
}

这里定义了三个环境下的不同数据库配置,在构建脚本中使用如下的代码来加载:

ext {
    profile = project.hasProperty('profile') ? project['profile'] : 'development' // 默认开发环境
}

def loadGroovyConfig() {
    def configFile = file('config.groovy')
    def config = new ConfigSlurper(profile).parse(configFile.toURL()).toProperties()
    return config
}

def jdbcConfig = loadGroovyConfig().getProperties().get('jdbc')
  • 推荐理由:Groovy 配置文件具有更好的可读性和结构,适合管理复杂的配置信息。对于新项目,建议使用 Groovy 配置文件。

6.3.2.2占位符替换

1. 占位符标注

  • 在资源文件中,使用 @key@ 的形式标注需要被替换的占位符。例如,在 jdbc.properties 文件中:

jdbc.url=@jdbc.url@
jdbc.user=@jdbc.user@
jdbc.password=@jdbc.password@

2. 使用 processResources Task

  • Gradle 提供了 processResources Task,用于处理资源文件。默认情况下,它会将 src/main/resources 目录下的资源文件复制到 build/resources/main 目录下。

  • 可以通过配置 processResources Task,使其在复制过程中替换资源文件中的占位符。但是,对于复杂的项目结构或自定义的配置文件目录,需要自定义 Task。

3. 自定义 Task 替换占位符

  • 当配置文件存放在自定义目录(如 config 目录)时,需要定义一个自定义 Task 来替换占位符,并让 processResources Task 依赖于该自定义 Task。

  • 示例代码:

task replaceConfigFiles(type: Copy) {
    from("${rootProject.projectDir}/config") {
        include '**/*.properties'
        include '**/*.xml'
        filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: loadGroovyConfig().getProperties())
    }
    into "${buildDir}/resources/main/config"
}

processResources.dependsOn replaceConfigFiles

解释:replaceConfigFiles Task 会将 config 目录下的 .properties.xml 文件复制到 build/resources/main/config 目录下,并在复制过程中替换占位符。然后,让 processResources Task 依赖于 replaceConfigFiles Task,以确保在资源处理之前完成占位符替换。

6.3.2.3 完整示例
apply plugin: 'java' // 或者 'war'、'application' 等

ext {
    profile = project.hasProperty('profile') ? project['profile'] : 'development'
}

def loadGroovyConfig() {
    def configFile = file('config.groovy')
    def config = new ConfigSlurper(profile).parse(configFile.toURL()).toProperties()
    return config
}

task replaceConfigFiles(type: Copy) {
    from("${rootProject.projectDir}/config") {
        include '**/*.properties'
        include '**/*.xml'
        filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: loadGroovyConfig().getProperties())
    }
    into "${buildDir}/resources/main/config"
}

processResources.dependsOn replaceConfigFiles

jar {
    // 确保替换后的配置文件被包含在 JAR 中
    from("${buildDir}/resources/main/config") {
        include '**/*.properties'
        include '**/*.xml'
    }
}

6.3.3声明项目依赖

在Gradle中,依赖管理是一个核心功能,它允许项目声明对其他项目、外部库或框架的依赖,并确保在构建过程中这些依赖被正确解析和包含。

  1. 项目依赖

对于多模块项目,Gradle允许在子项目之间声明依赖关系。这通过dependencies块中的project方法实现。例如:

project(':repository') {
    dependencies {
        implementation project(':model') // 使用implementation替代compile,以符合现代Gradle实践
    }
}

project(':web') {
    dependencies {
        implementation project(':repository')
        providedCompile 'javax.servlet:servlet-api:2.5' // 注意:providedCompile已被deprecated,使用compileOnly替代
        runtimeOnly 'javax.servlet:jstl:1.1.2' // 使用runtimeOnly替代runtime
    }
}

注意:compileruntime配置已被Gradle 7.x及更高版本弃用,建议使用implementationapicompileOnlyruntimeOnly等新的配置。

  1. Jar包依赖管理

Gradle支持从Maven仓库、Ivy仓库或本地文件系统解析Jar包依赖。依赖通过dependencies块中的implementationapicompileOnlyruntimeOnly等配置声明。例如:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:2.5.4'
    testImplementation 'junit:junit:4.13.2'
    compileOnly 'javax.servlet:servlet-api:2.5' // 仅在编译时使用,不打包进最终产品
    runtimeOnly 'com.h2database:h2:1.4.200' // 仅在运行时使用
}

Gradle会从配置的仓库中查找并下载这些依赖,以及它们的传递依赖(即依赖的依赖)。

  1. 子项目之间的依赖

在多模块项目中,子项目之间的依赖关系通过project方法声明。Gradle会自动处理这些依赖的解析和构建顺序。例如:

dependencies { implementation project(':core') // 依赖core子项目 }
  1. 构建脚本的依赖

构建脚本本身也可以有依赖,这通常用于引入非官方的Gradle插件或脚本库。这些依赖通过buildscript块声明:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.github.ben-manes:gradle-versions-plugin:0.29.0' // 引入版本插件
    }
}

// 应用插件
apply plugin: 'com.github.ben-manes.versions'
  1. 依赖解析机制和缓存管理

Gradle使用一种高效的依赖解析机制,它会根据项目的依赖关系图计算出最小的构建集,并尽可能重用缓存中的依赖项。这大大减少了构建时间和网络带宽的消耗。

  • 依赖解析:Gradle会解析项目的依赖关系图,并计算出需要构建和下载的所有依赖项。

  • 缓存管理:Gradle会将下载的依赖项和构建的产物缓存到本地文件系统中。在后续构建中,Gradle会首先检查缓存,如果缓存中存在所需的依赖项或产物,则直接使用,无需重新下载或构建。

  1. 部分构建和增量构建

为了进一步提高构建效率,Gradle支持部分构建和增量构建。

  • 部分构建:通过指定特定的子项目或任务进行构建,Gradle会仅构建这些指定的部分,而忽略其他未更改的部分。

  • 增量构建:Gradle会检测项目文件的变化,并仅重新构建那些发生变化的部分。这通过Gradle的输入/输出缓存机制实现。

  1. 依赖替换和排除

在某些情况下,你可能需要替换或排除某些依赖项。Gradle提供了灵活的机制来处理这些情况。

  • 替换依赖:使用resolutionStrategy块中的force方法可以强制使用特定版本的依赖项。

  • 排除依赖:使用exclude方法可以排除传递依赖中的某些项。

6.4 拆分项目文件

为了简化根项目的构建代码,可以将与特定子项目相关的代码移到相应的 build.gradle 文件中。

6.4.1 定义根项目的构建代码

根项目的 build.gradle 文件可以简化为只包含 allprojectssubprojects 配置块,如下所示:

allprojects {
    group = 'com.mlz.team'
    version = '1.0'
}

subprojects {
    apply plugin: 'java'
}
6.4.2 定义子项目的构建代码

将特定于子项目的构建代码移到相应的 build.gradle 文件中。如下所示:

// model 子项目的 build.gradle
// 无特定配置,因为已在根项目中应用 java 插件

// repository 子项目的 build.gradle
dependencies {
    implementation project(':model')
}

// web 子项目的 build.gradle
apply plugin: 'war'
apply plugin: 'jetty'

repositories {
    mavenCentral()
}

dependencies {
    implementation project(':repository')
    providedCompile 'javax.servlet:servlet-api:2.5'
    runtimeOnly 'javax.servlet:jstl:1.1.2'
}

6.5 自定义脚本

在多项目构建环境中,可以通过自定义脚本名称来避免混淆。这需要在 settings.gradle 文件中使用 buildFileName 属性来设置。如下所示:

// 通过目录来添加子项目
include 'todo-model', 'todo-repository', 'todo-web'

// 设置根项目的名字
rootProject.name = 'todo'

// 迭代访问所有根目录下的子项目,设置自定义的构建脚本名称
rootProject.children.each {
    it.buildFileName = "${it.name.replace('todo-', '')}.gradle"
}

通过这种方式,每个子项目都可以有独特的构建脚本名称,如 model.gradlerepository.gradleweb.gradle

6.6 指定Gradle版本

为了确保项目中使用的Gradle版本一致,Gradle Wrapper提供了一个便捷的方法。通过Wrapper,你可以在项目中指定一个Gradle版本,团队成员无论本地安装了哪个版本的Gradle,都可以使用项目指定的版本来构建项目。

6.6.1步骤:
  1. 在项目中创建Wrapper: 运行gradle wrapper命令。这将在项目的根目录下生成一个gradlew(Unix/Linux/macOS)和gradlew.bat(Windows)脚本,以及一个wrapper文件夹,里面包含Gradle Wrapper的Jar包和gradle-wrapper.properties文件。

  2. 配置Wrapper: 编辑gradle-wrapper.properties文件,指定Gradle的版本。例如:

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-x.y.z-bin.zip
  1. x.y.z替换为你想要使用的Gradle版本号。

  2. 使用Wrapper运行Gradle: 使用./gradlew(Unix/Linux/macOS)或gradlew.bat(Windows)脚本来运行Gradle任务,而不是直接使用gradle命令。Wrapper会下载并缓存指定的Gradle版本(如果尚未下载),并使用该版本来执行任务。

6.6.2 示例:

假设你想在项目中使用Gradle 7.4.2版本。

  1. 运行命令:

gradle wrapper --gradle-version=7.4.2

这将在项目根目录下生成必要的Wrapper文件和文件夹。

  1. 编辑gradle-wrapper.properties文件,确认distributionUrl属性如下:

distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
  1. 现在,你可以使用./gradlew(或gradlew.bat)来运行任何Gradle任务。例如:

./gradlew build

第一次运行时,Wrapper会下载Gradle 7.4.2版本并将其缓存到项目的gradle/dists目录下。之后的构建将直接使用这个缓存的版本。

通过这种方式,你可以确保整个团队都使用相同版本的Gradle来构建项目,从而避免由于版本差异导致的构建问题。

7 Gradle流水线配置

7.1 安装Gradle

  • 下载与安装:从Gradle官方网站下载适合您操作系统的Gradle安装包。

  • 配置环境变量:

    • Windows:将Gradle的bin目录添加到系统的PATH环境变量中。

    • macOS/Linux:通常可以通过在.bash_profile.zshrc或类似文件中添加export PATH="$PATH:/path/to/gradle/bin"来设置。

  • 验证安装:打开终端或命令提示符,输入gradle -v,如果显示Gradle的版本信息,则说明安装成功。

7.2 全局工具配置(在Jenkins中)

  • 登录Jenkins。

  • 进入“系统管理”(通常位于页面顶部的菜单中)。

  • 选择“全局工具配置”。

  • 在“Gradle安装”部分,添加一个新的Gradle安装条目,并指定Gradle的安装路径(即您解压Gradle包的目录)。

  • 保存配置。

7.3 插件安装(在Jenkins中)

  • 进入“系统管理”。

  • 选择“管理插件”。

  • 在“可用”标签页中,搜索“Gradle Plugin”。

  • 选中它并点击“安装无重启”或“下载并安装后重启”(根据您的需求)。

7.4 Jenkins流水线集成Gradle

在Jenkins流水线中集成Gradle通常涉及在Jenkinsfile中编写Pipeline脚本。以下是一个简单的示例:

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                // 从版本控制系统中检出代码
                checkout scm
            }
        }
        
        stage('Build') {
            steps {
                script {
                    // 使用Gradle构建项目
                    sh './gradlew build' // 注意使用./gradlew来确保使用项目内的Gradle Wrapper
                }
            }
        }
        
        // 可以添加更多阶段,如测试、部署等
    }
    
    post {
        always {
            // 清理工作区(可选)
            cleanWs()
        }
        success {
            // 构建成功后的操作(可选)
        }
        failure {
            // 构建失败后的操作(可选)
        }
    }
}

注意:

  • 使用./gradlew而不是gradle可以确保使用项目自带的Gradle Wrapper,这样可以避免版本不一致的问题。

  • Checkout阶段,scm是一个占位符,它会自动根据您在Jenkins项目配置中设置的源码管理设置来检出代码。

  • 您可以在post部分添加构建后操作,如发送通知、归档构建产物等。

三、NPM构建:前端工程化的加速器

NPM(Node Package Manager)作为Node.js的包管理器,在前端工程化中发挥着不可替代的作用。它不仅简化了依赖包的管理,还促进了前端开发的组件化、模块化和自动化。

  • 依赖管理:NPM通过package.json文件管理项目依赖,支持安装、更新和删除依赖包,确保项目环境的一致性和稳定性。

  • 脚本执行:NPM支持在package.json中定义自定义脚本,如构建、测试、启动等,提高了开发效率和便捷性。

  • 生态系统:NPM拥有丰富的开源库和工具,如Webpack、Babel等,支持前端项目的构建、打包、优化等各个环节。

1 NPM基础

NPM允许开发者发布、安装和管理Node.js项目中的依赖包。通过NPM,我们可以轻松地将各种优秀的第三方库和工具集成到项目中,从而提高开发效率和代码质量。

2 package.json文件

package.json是NPM项目的核心配置文件,它包含了项目的元数据以及依赖信息。以下是对package.json文件及其关键属性的详细解析:

  • name:项目名称,必须唯一且符合NPM的命名规范。

  • version:项目版本号,遵循语义化版本控制规范(SemVer)。

  • description:项目的简短描述。

  • main:项目入口文件的路径,通常是项目的JavaScript文件。

  • scripts:自定义脚本命令,用于执行项目构建、测试等任务。

  • dependencies:生产环境依赖的包,这些包在项目运行时是必需的。

  • devDependencies:开发环境依赖的包,这些包在项目开发过程中使用,但在生产环境中不需要。

  • repository:项目的仓库地址,通常用于指定Git仓库的URL。

  • keywords:项目的关键词,有助于在NPM上搜索到该项目。

  • author:项目作者的信息。

  • license:项目的许可证类型。

3 目录结构

一个高效、可维护的前端项目需要有一个合理的目录结构。以下是一个推荐的目录结构示例,它结合了NPM包管理的最佳实践:

my-project/
├── node_modules/          # 存放安装的依赖包
├── package.json           # 项目配置文件
├── package-lock.json      # 锁定依赖包的版本,确保项目的一致性
├── .npmignore             # 指定不包含在npm包中的文件和目录
├── bin/                   # 存放可执行二进制文件的目录(可选)
├── lib/                   # 存放JavaScript代码的地方
│   ├── index.js           # 项目入口文件
│   └── ...               # 其他JavaScript文件
├── doc/                   # 存放文档的目录
│   └── README.md          # 项目说明文档
├── test/                  # 存放单元测试用例的代码
│   └── ...               # 测试用例文件
├── config/                # 存放配置文件的目录(可选)
│   └── ...               # 配置文件
├── public/                # 存放静态资源的目录(如HTML、CSS、图片等,对于前端项目通常很重要)
│   └── ...               # 静态资源文件
└── src/                   # 存放源代码的目录(对于使用构建工具的项目,如Webpack,这是常见的结构)
    ├── components/        # 存放React/Vue等组件的目录
    ├── pages/             # 存放页面文件的目录
    ├── styles/            # 存放CSS/SCSS等样式文件的目录
    ├── utils/             # 存放工具函数的目录
    └── ...               # 其他源代码文件

4 项目初始化与依赖管理

4.1 项目初始化

使用npm init命令初始化项目,根据提示填写项目信息,生成package.json文件。这个文件是项目的核心配置文件,包含了项目的元数据、依赖、脚本等信息。

npm init

4.2 依赖管理

依赖管理是项目构建和开发的关键部分。使用 npm install 命令可以安装项目所需的依赖。根据项目需求,选择合适的工具、框架和库。

常见依赖:

  • 构建工具:如 Webpack、Vite 等,用于打包和构建项目。

  • 前端框架:如 React、Vue、Angular 等,用于构建用户界面。

  • 路由工具:如 React Router、Vue Router 等,用于管理页面路由。

  • 状态管理:如 Redux、Vuex 等,用于管理应用状态。

  • CSS 预编译器:如 Sass、Less 等,用于编写更高效的 CSS 代码。

  • UI 库:如 Bootstrap、Ant Design 等,用于快速构建用户界面。

  • 测试框架:如 Jest、Mocha 等,用于编写和运行测试。

5. 构建工具配置

5.1 Webpack 配置

Webpack 是一个功能强大的模块打包工具,广泛应用于现代 JavaScript 应用程序的开发中。为了充分利用 Webpack 的能力,我们需要仔细配置其各个方面,包括入口、输出、加载器和插件等。以下是对 Webpack 配置的深度优化指南:

(1)入口(Entry)

  • 单一入口:对于简单的项目,可以指定一个入口文件,如 src/index.jssrc/main.js

  • 多入口:对于复杂项目,可以配置多个入口点,以支持多页面应用(MPA)或库的开发。

示例:

entry: {
  main: './src/index.js',
  vendor: ['react', 'react-dom'] // 提取第三方库
},

(2)输出(Output)

  • 配置输出目录:确保打包后的文件放置在合适的目录中,如 dist

  • 文件名和路径:使用占位符(如 [name], [hash])来生成具有唯一标识的文件名,以便于缓存管理和版本控制。

示例:

output: {
  path: path.resolve(__dirname, 'dist'),
  filename: '[name].[contenthash].js',
  clean: true, // 自动清理未使用的文件
},

(3)加载器(Loaders)

  • Babel Loader:用于将 ES6+ 代码转换为向后兼容的 JavaScript 代码。

  • Vue Loader:用于处理 .vue 文件,将其编译为 JavaScript 模块。

  • CSS Loader + Style Loader:用于处理 CSS 文件,并将其注入到 DOM 中。

  • Less Loader:用于处理 Less 文件,将其编译为 CSS。

  • File Loader 和 URL Loader:用于处理文件和图片资源,根据文件大小决定是嵌入到代码中还是作为外部资源引用。

示例:

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    },
    {
      test: /\.vue$/,
      loader: 'vue-loader'
    },
    {
      test: /\.less$/,
      use: [
        'style-loader',
        'css-loader',
        {
          loader: 'less-loader',
          options: {
            lessOptions: {
              modifyVars: {},
              javascriptEnabled: true
            }
          }
        }
      ]
    },
    {
      test: /\.(png|jpe?g|gif|svg)$/i,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[path][name].[hash].[ext]',
          },
        },
      ],
    },
  ],
},

(4)插件(Plugins)

  • HtmlWebpackPlugin:用于自动生成 HTML 文件,并自动将打包后的资源注入到该文件中。

  • CleanWebpackPlugin:用于在每次打包前清理输出目录,避免旧文件的残留。

  • DefinePlugin:用于定义全局常量,以便在代码中直接使用环境变量。

  • MiniCssExtractPlugin:用于将 CSS 从 JavaScript 包中分离出来,生成独立的 CSS 文件。

  • TerserPlugin:用于压缩和优化 JavaScript 代码。

  • OptimizeCSSAssetsPlugin:与 TerserPlugin 配合使用,用于压缩 CSS 代码。

示例:

plugins: [
  new HtmlWebpackPlugin({
    template: './src/index.html',
    filename: 'index.html',
    minify: {
      collapseWhitespace: true,
      removeComments: true,
      removeRedundantAttributes: true,
      useShortDoctype: true,
      removeEmptyAttributes: true,
      removeStyleLinkTypeAttributes: true,
      keepClosingSlash: true,
      minifyJS: true,
      minifyCSS: true,
      minifyURLs: true,
    },
  }),
  new CleanWebpackPlugin(),
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
  }),
  new MiniCssExtractPlugin({
    filename: '[name].[contenthash].css',
  }),
  new TerserPlugin({
    terserOptions: {
      compress: {
        drop_console: true, // 去除console.log
      },
    },
  }),
  new OptimizeCSSAssetsPlugin({}),
],

5.2 环境变量配置

为了在不同环境下使用不同的配置,我们可以使用 dotenvcross-env 来管理环境变量。

(1)安装依赖

npm install dotenv cross-env --save-dev

(2)创建环境变量文件

在项目的根目录下创建 .env.dev.env.prod 文件,分别配置开发环境和生产环境的变量。

示例:

  • .env.dev

API_URL=http://localhost:3000/api
  • .env.prod

API_URL=https://example.com/api

(3)在 Webpack 配置文件中读取环境变量

在 Webpack 配置文件的顶部引入 dotenv 并加载相应的环境变量文件。

示例:

const dotenv = require('dotenv');
const webpack = require('webpack');

const env = process.env.NODE_ENV || 'development';
const envConfig = dotenv.config({ path: `./.env.${env}` }).parsed;

for (const key in envConfig) {
  process.env[key] = envConfig[key];
}

// 在 DefinePlugin 中使用这些环境变量
new webpack.DefinePlugin({
  'process.env': JSON.stringify(process.env),
}),

(4)在 package.json 中配置脚本

package.jsonscripts 部分配置用于启动开发服务器和构建生产环境的脚本。

示例:

"scripts": {
  "start": "cross-env NODE_ENV=development webpack serve --open --config webpack.config.js",
  "build": "cross-env NODE_ENV=production webpack --config webpack.config.js"
}

6 前端框架与路由配置

6.1. Vue框架配置

6.1.1 安装Vue及其相关依赖

首先,确保你已经安装了Vue 3及其相关依赖。使用npm或yarn进行安装,这里以npm为例:

npm install vue@next
npm install vue-loader@next @vue/compiler-sfc --save-dev
6.1.2 配置Webpack以支持Vue文件

在Webpack配置文件中添加对.vue文件的支持。通常,你会有一个webpack.config.js或类似的配置文件。在这个文件中,你需要添加vue-loader的规则,并引入VueLoaderPlugin

// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader/dist/index');

module.exports = {
  // ... 其他配置
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      // 其他加载器配置
    ]
  },
  plugins: [
    new VueLoaderPlugin()
    // 其他插件配置
  ]
};
6.1.3 配置Babel以支持ES6+

为了确保你的代码可以在所有现代浏览器中运行,你需要使用Babel将ES6+代码转换为ES5。

npm install @babel/core @babel/preset-env babel-loader --save-dev

然后,在Webpack配置文件中添加Babel加载器,并在项目根目录下创建babel.config.js文件。

// webpack.config.js
module.exports = {
  // ... 其他配置
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      // 其他加载器配置
    ]
  }
  // ... 其他配置
};

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        browsers: ['last 2 versions', 'not ie <= 11'] // 根据需要调整目标浏览器
      }
    }]
  ]
};
6.1.4 使用html-webpack-plugin处理HTML文件

安装html-webpack-plugin并配置它,以便自动生成index.html文件并插入打包后的资源。

npm install html-webpack-plugin --save-dev
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // ... 其他配置
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html', // 模板文件路径
      filename: 'index.html',
      minify: {
        // 压缩选项,根据需要调整
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
    }),
    // 其他插件配置
  ]
};
6.1.5 添加Vue Router

安装Vue Router并配置路由规则。

npm install vue-router@next --save

src/router/index.js中定义路由规则,并在src/main.js中挂载路由。

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  // 其他路由规则
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
});

export default router;

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

createApp(App)
  .use(router)
  .mount('#app');
6.1.6 添加Less预处理器

安装Less及其加载器,并在Webpack配置中添加相应的规则。

npm install style-loader css-loader less less-loader --save-dev
// webpack.config.js
module.exports = {
  // ... 其他配置
  module: {
    rules: [
      // ... 其他加载器配置
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                // Less选项,根据需要调整
              }
            }
          }
        ]
      }
    ]
  }
};

6.2. 路由配置

6.2.1 路由懒加载

使用动态导入来实现路由的懒加载,以减少初始加载时间。

// src/router/index.js
const Home = () => import(/* webpackChunkName: "home" */ '../views/Home.vue');
const About = () => import(/* webpackChunkName: "about" */ '../views/About.vue');

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
  // 其他路由规则
];
6.2.2 路由守卫

使用路由守卫来处理导航前的逻辑,如权限验证、页面跳转等。

// src/router/index.js
router.beforeEach((to, from, next) => {
  // 权限验证逻辑
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 判断用户是否已登录
    if (!isUserLoggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      });
    } else {
      next();
    }
  } else {
    next(); // 确保一定要调用 next()
  }
});

2.3 路由元信息

在路由规则中添加元信息(meta),以便于在路由守卫或其他地方使用。

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    meta: { title: 'Home Page' }
  },
  {
    path: '/about',
    name: 'About',
    component: About,
    meta: { title: 'About Page' }
  },
  // 其他路由规则
];

2.4 滚动行为

配置路由的滚动行为,以控制页面在导航时的滚动位置。

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition;
    } else {
      return { x: 0, y: 0 };
    }
  }
});

7 代码规范与自动化检测

7.1 代码规范

使用Prettier和ESLint配置代码规范。Prettier用于格式化代码,支持多种语言;ESLint用于检测代码中的潜在问题,提高代码质量。在根目录下创建.prettierrc.eslintrc.js文件,配置代码格式化规则和ESLint规则。

7.2 自动化检测

使用Git Hook工具(如husky)在git提交阶段触发ESLint检测,确保提交的代码符合规范。在package.json文件中配置husky和ESLint的脚本命令。

8 自动化部署

8.1 Jenkins服务器
  • 确保Jenkins服务器安装并配置:

    • 安装Jenkins:可以通过官方网站下载并安装Jenkins服务器。

    • 配置Jenkins:启动Jenkins服务后,通过浏览器访问Jenkins管理界面,并进行初始配置,如管理员账户、插件安装等。

  • 安装npm插件:

    • 在Jenkins管理界面,进入插件管理,搜索并安装NodeJS Plugin,该插件提供了npm的支持。

8.2 前端项目
  • 项目初始化与配置:

    • 使用npm进行项目初始化,确保package.json文件配置正确,包含所有必要的依赖和构建脚本。

    • 选择Vue、React等现代前端框架,并配置Webpack等构建工具。

8.3 Nginx服务器
  • 安装与配置Nginx:

    • 使用包管理器(如apt-get、yum等)安装Nginx。

    • 配置Nginx作为前端服务的静态文件服务器,修改Nginx配置文件以指定静态文件目录和默认索引页。

8.4 Jenkins构建流程
  1. 创建Jenkins任务:

    1. 在Jenkins上创建一个新的任务,选择“Freestyle project”或“Pipeline project”。

  2. 配置源码管理:

    1. 在“Source Code Management”部分,选择适合你的版本控制系统(如Git),并配置好仓库地址、分支和访问权限。

  3. 配置构建触发器:

    1. 根据需要配置构建触发器,GitLib hook trigger(代码提交后触发构建)。

  4. 配置构建步骤:

    npm install # 安装项目依赖
    npm run build # 执行构建命令(确保package.json中有对应的脚本)
    1. 添加“Execute shell”或“Execute Windows batch command”构建步骤,根据你的操作系统选择。

    2. 在命令框中输入以下命令用于编译前端项目:

    3. 你可以根据需要添加其他构建步骤,如代码质量检查、单元测试等。

  5. 配置构建后操作:

    1. 在“Post-build Actions”部分,配置构建后的操作,如使用SSH插件或scp命令将构建好的文件部署到Nginx服务器。

    2. 可以通过配置“Send files or execute commands over SSH”插件来实现文件的远程传输和部署。

  6. 流水线配置(如果创建任务用了 Pipeline project)

pipeline {
    agent any // 或者指定一个具有必要工具和环境的代理

    environment {
        // 定义环境变量,例如NGINX服务器的部署路径
        NGINX_DEPLOY_PATH = '/var/www/your-app' // 请根据您的实际情况修改
        // 如果需要指定Node.js版本,也可以在这里设置(但通常这应该在Jenkins节点配置中完成)
    }

    stages {
        stage('Checkout') {
            steps {
                // 从GitLab检出代码
                checkout scm // scm是Jenkins内置的一个步骤,它会根据配置的源代码管理仓库检出代码
                // 如果您需要指定GitLab的特定分支或仓库URL,可以在Jenkins的作业配置中设置
            }
        }

        stage('Build') {
            steps {
                script {
                    // 在Jenkins上编译代码
                    // 假设您的项目是一个Node.js项目,并且有一个package.json文件
                    sh 'npm install' // 安装项目依赖
                    sh 'npm run build' // 执行构建脚本(根据您的项目实际情况可能是其他命令)
                    
                    // 如果构建产物不在当前目录,您可能需要将它们复制到某个特定目录
                    // 例如:sh 'cp -r dist/* ${WORKSPACE}/build-output/'
                }
            }
        }

        stage('Deploy') {
            steps {
                script {
                    // 将构建产物部署到NGINX服务的目录
                    // 注意:这里假设Jenkins有权限写入NGINX的部署路径
                    // 如果不是,您可能需要设置适当的权限或使用SSH/SCP等步骤来传输文件
                    sh "cp -r ${WORKSPACE}/dist/* ${NGINX_DEPLOY_PATH}/" // 根据您的实际构建产物目录修改
                    
                    // 如果您需要重启NGINX来使更改生效,可以添加以下步骤(但请谨慎使用,因为这可能会导致服务中断)
                    // sh 'sudo systemctl restart nginx' // 请确保Jenkins用户有执行此命令的权限
                    
                    // 或者,如果您不想在Pipeline中直接重启NGINX,可以发送一个通知给负责重启的人或系统
                }
            }
        }
    }

    post {
        success {
            // 构建成功后的操作,例如发送通知
            echo 'Deployment successful!'
            // 可以添加其他通知步骤,如发送邮件、Slack消息等
        }
        failure {
            // 构建失败后的操作,例如发送通知
            echo 'Deployment failed!'
            // 可以添加错误日志记录、发送失败通知等步骤
        }
    }
}
8.5 Nginx配置
  1. 配置Nginx静态文件服务:

    1. 打开Nginx配置文件(通常位于/etc/nginx/nginx.conf/etc/nginx/sites-available/default)。

    2. 配置一个server块,用于处理前端静态文件,如:

server {
        listen 80;
        server_name your_domain.com; # 替换为你的域名

        location / {
                root /path/to/your/nginx/static/files; # 替换为你的静态文件目录
                index index.html;
                try_files $uri $uri/ /index.html; # 解决单页应用刷新404问题
        }

        # 其他配置...
}
  1. 部署前端静态文件:

    1. 将Jenkins构建好的前端静态文件通过SSH或其他方式传输到Nginx服务器的指定目录。

    2. 确保文件权限和所有权设置正确,以便Nginx能够正确读取文件。

四、Gradle or maven

在为后端构建选择Gradle还是Maven时,需要综合考虑多个因素,包括性能、灵活性、易用性、插件生态等。以下是对两者的比较:

1 性能

  • Gradle:

    • Gradle通常比Maven更快,尤其是在处理大型项目和子项目时。

    • Gradle实现了增量构建机制、构建缓存机制和守护进程机制等策略来保证构建速度。

    • 增量构建可以分析源文件和类文件之间的依赖关系,并只重新编译改变的部分。

    • Gradle的智能类路径分析器可以避免不必要的编译,当二进制接口没有改变时,不会重新编译。

  • Maven:

    • Maven的构建速度相对较慢,尤其是在处理大型项目和复杂依赖时。

    • Maven也支持增量构建和构建缓存,但相比之下效果可能不如Gradle显著。

2 灵活性

  • Gradle:

    • Gradle具有非常强的灵活性,可以满足各种自定义需求。

    • Gradle使用基于Groovy或Kotlin的DSL来声明项目设置,相比Maven的XML配置更加简洁和易读。

    • Gradle允许开发者自定义构建逻辑和任务,更适合需要动态配置的复杂项目。

  • Maven:

    • Maven的灵活性相对较低,遵循固定的项目结构和生命周期。

    • Maven的XML配置文件相对冗长,且限制了开发者的自定义能力。

3 易用性

  • Gradle:

    • Gradle的学习曲线可能更陡峭,需要掌握Groovy或Kotlin语言和Gradle的构建脚本编写方法。

    • 但对于熟悉Groovy或Kotlin的开发者来说,Gradle的易用性会更高。

  • Maven:

    • Maven的学习曲线相对较低,XML语法易于理解。

    • Maven的项目结构和生命周期固定,使得初学者更容易上手。

4 插件生态

  • Gradle:

    • Gradle拥有丰富的插件生态系统,但相比Maven来说插件数量可能稍少。

    • Gradle支持自定义插件的开发,可以满足更多特定的需求。

  • Maven:

    • Maven拥有更加成熟和丰富的插件生态系统,可以满足更多的构建需求。

    • Maven的插件数量众多,且有很多高质量的插件可供选择。

5 总结与选择建议

  1. 如果项目需要高性能和灵活性:

    1. 选择Gradle作为构建工具。Gradle的增量构建、构建缓存和守护进程机制可以显著提高构建速度。

    2. Gradle的灵活性和可扩展性可以满足各种自定义需求。

  2. 如果项目需要易于理解和上手:

    1. 选择Maven作为构建工具。Maven的项目结构和生命周期固定,使得项目相对容易看懂。

    2. Maven的XML配置文件虽然冗长,但易于理解和维护。

  3. 如果项目需要丰富的插件支持:

    1. 也可以考虑选择Maven作为构建工具。Maven的插件生态系统更加成熟和丰富。


原文地址:https://blog.csdn.net/heijunwei/article/details/144306490

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!