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

发布于:2024-12-18 ⋅ 阅读:(206) ⋅ 点赞:(0)

在快速迭代的软件工程领域,项目构建工具扮演着举足轻重的角色。它们不仅自动化了构建、测试、打包和部署等关键环节,还显著提升了开发效率和质量。本文将深入探讨后端常用的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的插件生态系统更加成熟和丰富。


网站公告

今日签到

点亮在社区的每一天
去签到