网站后台换图片/国内营销推广渠道
在快速迭代的软件工程领域,项目构建工具扮演着举足轻重的角色。它们不仅自动化了构建、测试、打包和部署等关键环节,还显著提升了开发效率和质量。本文将深入探讨后端常用的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 的构建过程主要包含以下环节:
-
清理(clean):删除构建过程中生成的文件,如
target
目录下的内容。 -
编译(compile):将 Java 源代码编译成字节码文件(.class 文件)。
-
测试(test):运行测试代码,验证项目的正确性。
-
打包(package):将编译后的字节码文件和资源文件打包成 JAR 或 WAR 文件。
-
安装(install):将打包后的文件安装到本地仓库中,供其他项目使用。
-
部署(deploy):将打包后的文件部署到远程仓库中,供其他团队或项目使用。
这些环节可以通过 Maven 的生命周期进行管理,每个生命周期阶段都有对应的目标(goal)可以执行。
2 依赖管理
Maven 中最关键的部分是依赖管理。使用 Maven 最主要的就是使用它的依赖管理功能,它可以帮助我们解决以下问题:
-
jar 包的下载:使用 Maven 之后,jar 包会从规范的远程仓库(如 Maven 中央仓库)下载到本地仓库中。这样,我们就无需手动下载和管理这些 jar 包了。
-
jar 包之间的依赖:通过依赖的传递性自动完成。例如,如果项目 A 依赖项目 B,而项目 B 又依赖项目 C,那么 Maven 会自动下载并引入项目 C 的 jar 包。
-
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 本地安装与配置环境变量
-
将下载的 Maven 安装文件解压到你指定的目录中,例如
D:\bin\apache-maven-3.x
。 -
设置环境变量:
-
新建一个系统变量,变量名为
M2_HOME
,变量值为 Maven 的安装目录,例如D:\bin\apache-maven-3.x
。 -
在系统变量中找到名为
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 创建父工程
-
使用 Maven 命令或 IDE 创建一个新的 Maven 项目。
-
在
pom.xml
文件中,将<packaging>
标签的值设置为pom
,表示这是一个父工程。 -
(可选)删除生成的
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/mydbusername: prod-userpassword: 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 插件
-
安装插件:
-
在 IntelliJ IDEA 中,打开
Settings
(或Preferences
在 macOS 上)。 -
导航到
Plugins
,搜索Maven Helper
并安装它。
-
-
使用插件查找冲突:
-
打开项目的
pom.xml
文件。 -
右键点击
pom.xml
文件,选择Maven
->Show Dependencies
。 -
在弹出的窗口中,选择
Conflicts
标签页。 -
这里将列出所有冲突的 JAR 包及其不同版本和来源。
-
-
解决冲突:
-
根据冲突信息,决定使用哪个版本的 JAR 包。
-
在
pom.xml
文件中,使用<exclusions>
标签排除不需要的版本。 -
或者,使用
<dependencyManagement>
在父 POM 中统一管理依赖版本。
-
4.8.2 使用 Maven 的 enforcer 插件
Maven Enforcer 插件可以帮助检测项目中的潜在问题,包括依赖冲突和类路径中的重复类。
-
配置 enforcer 插件:
-
在项目的
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
规则可能需要额外的配置来指定哪些包或类应该被检查。此外,这个规则可能会比较耗时,因为它需要扫描整个类路径。
-
运行 enforcer 插件:
-
在命令行中,运行
mvn enforcer:enforce
来执行插件。 -
如果检测到问题,插件将输出错误信息,包括冲突的依赖或重复的类。
-
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 注意事项
-
确保Jenkins服务器已经安装了Maven,并且Maven的路径已经配置在Jenkins的全局工具配置中(虽然直接在Pipeline脚本中指定路径也可以)。
-
如果项目有特定的Maven设置(如settings.xml),可以在Pipeline脚本中指定
--settings
参数来覆盖默认设置。 -
根据项目的需要,可以在
mvn
命令后添加其他参数,如-DskipTests=true
来跳过测试等。
二、Gradle构建:灵活性与扩展性的典范
Gradle,作为一款开源的自动化构建工具,凭借其强大的多语言支持能力,以及对Apache Ant和Maven的精髓融合,已经在构建工具领域崭露头角。与Ant的随意性和Maven的繁琐配置及严格生命周期相比,Gradle以其规范性和更高的灵活性,为开发者带来了全新的构建体验。
作为开发者,我们深知项目自动构建在日常工作中的重要性。想象一下,如果构建代码能像源代码一样易于扩展、测试和维护,那将极大地提升我们的工作效率。Gradle正是为此而生,它采用DSL(领域特定语言,如Groovy)来编写构建脚本,使得脚本更加简洁明了,易于理解和维护。
Gradle的构建脚本是声明式的,能够清晰地表达构建意图。与XML相比,使用Groovy编写构建脚本大大减少了代码量,提高了可读性。更重要的是,Gradle还集成了其他构建工具,如Ant和Maven,使得原有项目能够轻松迁移到Gradle平台,无需进行大规模的改造。
-
Gradle的优势:
-
灵活性强:Gradle提供丰富的API,允许用户根据需求定制项目的构建过程。相较于Maven和Ant,Gradle的构建脚本更加灵活且易于编写。
-
粒度精细:在Gradle中,源码编译、资源编译等任务都是通过Task来完成的。用户可以自由修改Task,实现对构建过程的精细控制。
-
扩展性高:Gradle支持插件机制,用户只需简单配置即可复用这些插件,从而轻松扩展Gradle的功能和适用范围。
-
兼容性好:Gradle不仅功能强大,还能完美兼容Maven和Ant的功能。这意味着用户可以在Gradle中继续使用他们熟悉的Maven和Ant构建脚本和插件。
-
趋势引领:众多大厂的开源代码已经开始转向Gradle管理,这已成为构建工具领域的一大趋势。
-
Gradle的劣势:
-
版本兼容性:虽然Gradle的每个版本都可能带来较大的改动,但Gradle团队正在逐步改善这一问题。同时,使用Gradle Wrapper可以确保团队成员使用相同版本的Gradle,减少因版本差异带来的问题。
-
学习成本:对于不熟悉Groovy的用户来说,学习成本可能较高。但Gradle也提供了Kotlin DSL作为替代方案,降低了学习难度。此外,Gradle社区和文档资源也非常丰富,有助于用户快速上手。
-
踩坑成本:由于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内置了一些闭包(如build
、dependencies
等)和插件提供的闭包(如war
、java
等),用户可以在这些闭包中编写自定义的构建逻辑。
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 测试代码目录
-
build.gradle
(①):-
这是Gradle构建脚本的核心文件,用于定义项目的构建逻辑、依赖关系、插件等。
-
全局
build.gradle
文件通常位于项目根目录,用于设置全局性的配置,如仓库源和Gradle版本号。 -
模块内的
build.gradle
文件则负责该模块的特定构建配置。
-
-
gradlew
(②):-
这是Linux/Unix/Mac系统下的Gradle Wrapper脚本,允许用户在不手动安装Gradle的情况下执行Gradle构建任务。
-
通过运行
./gradlew
命令,用户可以轻松触发构建过程。
-
-
gradlew.bat
(③):-
这是Windows系统下的Gradle Wrapper脚本,功能与
gradlew
类似,但专为Windows环境设计。 -
用户只需双击
gradlew.bat
或在命令行中执行该脚本,即可启动Gradle构建。
-
-
settings.gradle
(④):-
此文件用于定义项目的全局设置,如项目名称、包含的模块等。
-
无论项目包含多少个子模块,
settings.gradle
文件都只有一个,且必须位于根项目目录中。
-
-
gradle/wrapper
(⑤):-
此目录包含Gradle Wrapper所需的两个关键文件:
gradle-wrapper.jar
和gradle-wrapper.properties
。 -
gradle-wrapper.jar
负责实际的Wrapper逻辑,而gradle-wrapper.properties
则存储了Wrapper的配置信息,如Gradle版本等。 -
通过Wrapper,Gradle可以自动下载并安装指定版本的Gradle环境,从而简化版本管理。
-
-
src
(⑥):-
这是源代码的存放目录,通常包含
main
(主代码)和test
(测试代码)两个子目录。 -
main
目录用于存放项目的核心代码和资源文件,而test
目录则用于存放单元测试代码。
-
3.2 build.gradle
文件详解
build.gradle
文件是 Gradle 构建系统中的一个核心配置文件,用于定义项目的构建脚本。Gradle 是一个开源的自动化构建工具,它允许你以声明性的方式定义项目的构建逻辑,包括依赖管理、任务执行等。build.gradle
文件通常包含以下内容:
-
插件应用:通过
plugins
块应用 Gradle 插件,这些插件为 Gradle 添加了额外的功能,如 Java 编译、测试运行、打包等。 -
项目信息:定义项目的组名(
group
)、版本号(version
)、Java 版本兼容性(sourceCompatibility
和targetCompatibility
)等。 -
仓库配置:指定 Gradle 查找依赖项的仓库地址,如 Maven 中央仓库、私有仓库等。
-
依赖管理:在
dependencies
块中声明项目所需的依赖项,Gradle 会自动从配置的仓库中下载这些依赖项。 -
任务配置:定义和配置 Gradle 任务,这些任务可以是自定义的,也可以是插件提供的。
-
其他配置:如构建脚本依赖(
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 属性
-
内置属性:
-
可以直接赋值,无需声明。
-
例如:
group = 'com.mlz'
和version = '1.0.0'
。
-
-
自定义属性:
-
可以使用Groovy语法或Java语法结合。
-
Groovy定义属性:
def pname = "projectName:${project.name}"
-
Java类型接收:
String pname = "projectName:${project.name}"
-
-
扩展属性:
-
使用
ext
命名空间来扩展属性,定义后可以在project、task、subproject中读取和更新。 -
例如:
ext.prop1 = 'hjw'
和ext.prop2 = 'gradle'
。
-
-
属性作用域:
-
Project对象自身:包含所有通过getters和setters方法定义的属性。
-
ext属性(extra):一个包含任意名称-值对的映射。
-
插件扩展属性(extensions):每个扩展名都可以作为只读属性使用。
-
插件约定属性(convention):通过Convention对象添加到Project中的属性。
-
Tasks:使用任务名称作为属性名称来访问任务,只读。
-
继承属性:ext的属性和约定属性从项目的父级继承,递归到根项目,只读。
-
-
常用project属性:
-
allprojects
:包含此项目及其子项目的集合。 -
buildDir
:当前项目的编译目录,默认值为projectDir/build
。 -
defaultTasks
:当前项目的默认任务集合。 -
group
:当前项目的组名。 -
logger
:当前项目的日志器。 -
name
:此项目的名称。 -
parent
:此项目的父项目。 -
path
:此项目的绝对路径。 -
project
:当前project对象实例。 -
rootDir
:本项目的根目录。 -
rootProject
:当前项目层次结构中的根project。 -
subprojects
:当前项目的子项目集合。 -
tasks
:本项目的task集合。 -
version
:此项目的版本。
-
3.3.2 方法
-
方法作用域:
-
Project对象自身的方法。
-
build.gradle
脚本文件中定义的方法。 -
通过插件添加到Project中的扩展方法。
-
插件添加到项目中的约定方法。
-
项目中的Tasks,每个Task都会添加一个方法,方法名是任务名。
-
-
常用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
)。使用二进制插件有两种方式:
-
结合
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:+'}
}
-
使用
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/java
、rootProjectDir/buildSrc/src/main/groovy
或rootProjectDir/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
实现了TaskCollection
和PolymorphicDomainObjectContainer
,因此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的基类,如Copy
、Delete
等。
task copyTask(type: Copy) {from 'src/main/resources'into 'build/resources/main'
}
4.5 Task依赖与排序
-
Task依赖
task hello {doLast { println 'Hello!' }
}task intro {dependsOn hellodoLast { 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.Durationtask 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实例。
具体流程:
-
创建Settings实例:Gradle首先读取并解析
settings.gradle
文件,创建一个Settings实例。这个实例定义了项目的全局设置,例如包含哪些子项目。 -
解析并加载Project:根据Settings实例的配置,Gradle会扫描并解析
settings.gradle
中指定的项目。为每个项目创建对应的Project实例,这些项目的加载顺序是按照配置的层次结构进行的,确保父项目在子项目之前被加载。 -
初始化Project实例:值得注意的是,虽然此阶段会涉及
build.gradle
文件,但初始化Project实例时并不会深入解析build.gradle
文件中的具体配置。这一步主要是为项目创建基本的框架和实例。
4.2 配置阶段
任务:解析所有project中的build.gradle
文件,获取所有的task,构建任务依赖图。
具体流程:
-
执行build.gradle脚本:Gradle会循环执行每个项目的
build.gradle
脚本文件。这个过程中,Gradle会解析脚本中的配置,注册任务(Task),并确定任务之间的依赖关系。 -
构建任务依赖图:在解析完所有的
build.gradle
文件后,Gradle会根据任务之间的依赖关系构建一个任务依赖图(Task Dependency Graph)。这个图是一个有向无环图(DAG),表示了任务之间的执行顺序和依赖关系。 -
任务配置:在配置阶段,Gradle还会对每个任务进行初始化配置,包括设置任务的输入、输出和动作等。
4.3 执行阶段
任务:按照任务依赖图执行所有指定的task。
具体流程:
-
任务调度:Gradle会根据任务依赖图确定需要执行的任务及其执行顺序。如果某个任务的输入是另一个任务的输出,则该任务必须在另一个任务之后执行。
-
任务执行:Gradle会按照确定的顺序执行任务。在执行每个任务之前,Gradle会检查该任务的输入是否发生了变化。如果输入没有变化,并且任务的结果仍然是最新的,则Gradle会跳过该任务的执行,以提高构建效率。
-
任务完成:当所有任务都执行完毕后,Gradle会触发相应的钩子函数,表示构建过程已经完成。
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
文件:
-
查找与当前目录具有相同嵌套级别的名为
settings.gradle
的文件,但位于一个名为master
的目录中(这个行为可能因Gradle版本而异)。 -
如果在第一步中没有找到
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.properties
、test.properties
和production.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 replaceConfigFilesjar {// 确保替换后的配置文件被包含在 JAR 中from("${buildDir}/resources/main/config") {include '**/*.properties'include '**/*.xml'}
}
6.3.3声明项目依赖
在Gradle中,依赖管理是一个核心功能,它允许项目声明对其他项目、外部库或框架的依赖,并确保在构建过程中这些依赖被正确解析和包含。
-
项目依赖
对于多模块项目,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}
}
注意:compile
和runtime
配置已被Gradle 7.x及更高版本弃用,建议使用implementation
、api
、compileOnly
和runtimeOnly
等新的配置。
-
Jar包依赖管理
Gradle支持从Maven仓库、Ivy仓库或本地文件系统解析Jar包依赖。依赖通过dependencies
块中的implementation
、api
、compileOnly
、runtimeOnly
等配置声明。例如:
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会从配置的仓库中查找并下载这些依赖,以及它们的传递依赖(即依赖的依赖)。
-
子项目之间的依赖
在多模块项目中,子项目之间的依赖关系通过project
方法声明。Gradle会自动处理这些依赖的解析和构建顺序。例如:
dependencies { implementation project(':core') // 依赖core子项目 }
-
构建脚本的依赖
构建脚本本身也可以有依赖,这通常用于引入非官方的Gradle插件或脚本库。这些依赖通过buildscript
块声明:
buildscript {repositories {mavenCentral()}dependencies {classpath 'com.github.ben-manes:gradle-versions-plugin:0.29.0' // 引入版本插件}
}// 应用插件
apply plugin: 'com.github.ben-manes.versions'
-
依赖解析机制和缓存管理
Gradle使用一种高效的依赖解析机制,它会根据项目的依赖关系图计算出最小的构建集,并尽可能重用缓存中的依赖项。这大大减少了构建时间和网络带宽的消耗。
-
依赖解析:Gradle会解析项目的依赖关系图,并计算出需要构建和下载的所有依赖项。
-
缓存管理:Gradle会将下载的依赖项和构建的产物缓存到本地文件系统中。在后续构建中,Gradle会首先检查缓存,如果缓存中存在所需的依赖项或产物,则直接使用,无需重新下载或构建。
-
部分构建和增量构建
为了进一步提高构建效率,Gradle支持部分构建和增量构建。
-
部分构建:通过指定特定的子项目或任务进行构建,Gradle会仅构建这些指定的部分,而忽略其他未更改的部分。
-
增量构建:Gradle会检测项目文件的变化,并仅重新构建那些发生变化的部分。这通过Gradle的输入/输出缓存机制实现。
-
依赖替换和排除
在某些情况下,你可能需要替换或排除某些依赖项。Gradle提供了灵活的机制来处理这些情况。
-
替换依赖:使用
resolutionStrategy
块中的force
方法可以强制使用特定版本的依赖项。 -
排除依赖:使用
exclude
方法可以排除传递依赖中的某些项。
6.4 拆分项目文件
为了简化根项目的构建代码,可以将与特定子项目相关的代码移到相应的 build.gradle
文件中。
6.4.1 定义根项目的构建代码
根项目的 build.gradle
文件可以简化为只包含 allprojects
和 subprojects
配置块,如下所示:
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.gradle
、repository.gradle
和 web.gradle
6.6 指定Gradle版本
为了确保项目中使用的Gradle版本一致,Gradle Wrapper提供了一个便捷的方法。通过Wrapper,你可以在项目中指定一个Gradle版本,团队成员无论本地安装了哪个版本的Gradle,都可以使用项目指定的版本来构建项目。
6.6.1步骤:
-
在项目中创建Wrapper: 运行
gradle wrapper
命令。这将在项目的根目录下生成一个gradlew
(Unix/Linux/macOS)和gradlew.bat
(Windows)脚本,以及一个wrapper
文件夹,里面包含Gradle Wrapper的Jar包和gradle-wrapper.properties
文件。 -
配置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
-
将
x.y.z
替换为你想要使用的Gradle版本号。 -
使用Wrapper运行Gradle: 使用
./gradlew
(Unix/Linux/macOS)或gradlew.bat
(Windows)脚本来运行Gradle任务,而不是直接使用gradle
命令。Wrapper会下载并缓存指定的Gradle版本(如果尚未下载),并使用该版本来执行任务。
6.6.2 示例:
假设你想在项目中使用Gradle 7.4.2版本。
-
运行命令:
gradle wrapper --gradle-version=7.4.2
这将在项目根目录下生成必要的Wrapper文件和文件夹。
-
编辑
gradle-wrapper.properties
文件,确认distributionUrl
属性如下:
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
-
现在,你可以使用
./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 anystages {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.js
或src/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 环境变量配置
为了在不同环境下使用不同的配置,我们可以使用 dotenv
和 cross-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.json
的 scripts
部分配置用于启动开发服务器和构建生产环境的脚本。
示例:
"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构建流程
-
创建Jenkins任务:
-
在Jenkins上创建一个新的任务,选择“Freestyle project”或“Pipeline project”。
-
-
配置源码管理:
-
在“Source Code Management”部分,选择适合你的版本控制系统(如Git),并配置好仓库地址、分支和访问权限。
-
-
配置构建触发器:
-
根据需要配置构建触发器,GitLib hook trigger(代码提交后触发构建)。
-
-
配置构建步骤:
npm install # 安装项目依赖 npm run build # 执行构建命令(确保package.json中有对应的脚本)
-
添加“Execute shell”或“Execute Windows batch command”构建步骤,根据你的操作系统选择。
-
在命令框中输入以下命令用于编译前端项目:
-
你可以根据需要添加其他构建步骤,如代码质量检查、单元测试等。
-
-
配置构建后操作:
-
在“Post-build Actions”部分,配置构建后的操作,如使用SSH插件或scp命令将构建好的文件部署到Nginx服务器。
-
可以通过配置“Send files or execute commands over SSH”插件来实现文件的远程传输和部署。
-
-
流水线配置(如果创建任务用了 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配置
-
配置Nginx静态文件服务:
-
打开Nginx配置文件(通常位于
/etc/nginx/nginx.conf
或/etc/nginx/sites-available/default
)。 -
配置一个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问题}# 其他配置...
}
-
部署前端静态文件:
-
将Jenkins构建好的前端静态文件通过SSH或其他方式传输到Nginx服务器的指定目录。
-
确保文件权限和所有权设置正确,以便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 总结与选择建议
-
如果项目需要高性能和灵活性:
-
选择Gradle作为构建工具。Gradle的增量构建、构建缓存和守护进程机制可以显著提高构建速度。
-
Gradle的灵活性和可扩展性可以满足各种自定义需求。
-
-
如果项目需要易于理解和上手:
-
选择Maven作为构建工具。Maven的项目结构和生命周期固定,使得项目相对容易看懂。
-
Maven的XML配置文件虽然冗长,但易于理解和维护。
-
-
如果项目需要丰富的插件支持:
-
也可以考虑选择Maven作为构建工具。Maven的插件生态系统更加成熟和丰富。
-
相关文章:

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

【数据库】复习
数据库期中复习——概念填空_在修改数据结构时,为了保证数据库的数据独立性-CSDN博客 选择题 关系数据理论-数据库习题_数据库关系理论考题-CSDN博客 关系、关系模式、关系模型区别和联系 关系:元组的集合,一张表 关系模式:关系的描述 R(…...

CorsConfig前后端数据跨域连接,IDEA右侧Maven窗口消失
前后端数据跨域连接(分页查询并显示) 一、后端添加分页查询 分页查询核心就是:每页需要显示多少条记录(pageSize),当前查看第几页(pageNum);MySQL提供了分页函数limit m,n select * from table limit (pageNum-1)*pageSize, pa…...

Python微博动态爬虫
本文是刘金路的《语言数据获取与分析基础》第十章的扩展,详细解释了如何利用Python进行微博爬虫,爬虫内容包括微博指定帖子的一级评论、评论时间、用户名、id、地区、点赞数。 整个过程十分明了,就是用户利用代码模拟Ajax请求,发…...

【设计模式】单例模式 在java中的应用
文章目录 引言什么是单例模式单例模式的应用场景单例模式的优缺点优点缺点 单例模式的基本实现饿汉式单例模式懒汉式单例模式双重检查锁定静态内部类枚举单例 单例模式的线程安全问题多线程环境下的单例模式线程安全的实现方式1. **懒汉式单例模式(线程不安全&#…...

burp suite 8
声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...

为什么在Java中super与this不能共存于子类构造器中,其中this起什么作用
在 Java 中,super 和 this 是两个关键字,它们在子类的构造器中有特定的用途和限制。 super 关键字: super 用于从父类(超类)访问成员(属性和方法)或者调用父类的构造方法。 在子类的构造器中&…...

Hypothesis:高效的 Python 测试工具
简介:Hypothesis 是一个强大的 Python 测试库,旨在自动生成各种测试案例,以帮助开发者发现潜在的边界问题和隐藏的错误。通过对输入数据进行智能化的探索,Hypothesis 能够为测试提供更全面的覆盖,避免遗漏一些极端或不…...

Terminus Calculator 计算原理分享
在《使命召唤:黑色行动 6》僵尸模式中,Terminus 关卡的研究办公室里有一个复杂的数学谜题需要解决。为了获得多相共振器,玩家需要计算出三个数字并输入电脑。虽然可以花费 5000 精华来获得答案,但使用 Terminus Calculator 可以更…...

Wwise 使用MIDI文件、采样音频
第一种:当采样音频只有一个文件的时候 1.拖入MIDI文件到Interactive Music Hierarchy层级 2.拖入采样音频到Actor-Mixer Hierarchy层级 3.勾选MIDI显示出面板,设置Root Note与采样音频音高相同,这里是C#5 4.播放测试,成功&…...

在CentOS上无Parallel时并发上传.wav文件的Shell脚本解决方案
在CentOS上无Parallel时并发上传.wav文件的Shell脚本解决方案 背景概述解决方案脚本实现脚本说明使用指南注意事项在CentOS操作系统环境中,若需并发上传特定目录下的.wav文件至HTTP服务器,而系统未安装GNU parallel工具,我们可通过其他方法实现此需求。本文将介绍一种利用Sh…...

【RocketMQ 源码分析(一)】设计理念与源码阅读技巧
RocketMQ 的设计理念与源码阅读技巧 一、设计理念二、源码设计三、源码阅读技巧 一直想仔细仔细看看这个 RocketMQ 的源码,学学它的设计思想和编码风格,没准在以后自己在设计和编码的时候有思考的方向。这是专栏的第一篇 —— 介绍下 RocketMQ 的一些设计…...

独立ip服务器有什么优点?
网站的性能和安全性直接影响到用户体验和业务发,独立IP服务器作为一种主流的托管方式,因其独特的优势而受到许多企业和个人站长的青睐。与共享IP相比,独立IP服务器到底有哪些优点呢? 使用独立IP的用户不必担心与其他网站共享同一…...

如何使用Python库连接Redis
1、redis-py 库封装一个 Redis 工具类可以帮助我们简化 Redis 的操作并提高代码的复用性和可维护性。 安装redis pip install redisimport redis import logginglogging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__)class RedisUtils:def __init__(s…...

Vant UI +Golang(gin) 上传文件
前端基本用法:点击查看 实现代码: const afterRead (file) > {console.log(file);//set content-type to multipart/form-dataconst formData new FormData();formData.append("file", file.file);request.POST("/api/v1/users/up…...

【Unity高级】如何实现粒子系统的间歇式喷射
先看下要最终实现的效果: 代码如下: using UnityEngine; using System.Collections;public class ParticleBurstController : MonoBehaviour {private ParticleSystem _particleSystem; // 获取粒子系统public float burstDuration 2f; // 每次…...

通过linux命令获取自选股票价格及大盘涨跌幅
技术发展与数据获取需求 互联网与金融数据融合:随着互联网的普及和金融市场的数字化发展,金融数据的获取和分析变得更加便捷和重要。投资者希望能够及时、准确地获取股票价格和市场指数等信息,以便做出合理的投资决策。Linux 作为一种强大的操作系统,为数据获取和处理提供…...

透彻理解并解决Mockito模拟框架的单元测试无法运行的问题
本篇的实例基于Maven IDE (VS Code) 运行 在VS Code 运行的时候, 不需要在pom.xml 中添加任何插件就可以在测试类中看到如下的绿色按钮,单击就可以运行使用Mockito 注解 ExtendWith(MockitoExtension.class) 或是 Mockito 代码方式的测试。 不使用注…...

vue3字典数据的显示问题(使用hooks解决)
我们在使用 element-plus的时候,经常会使用一些字典数据, 在搜索框的时候,字典数数要使用 el-select el-option 来显示,当在table表格的时候,我们通常记录的是 字典数据的id , 又要把它改变成 字典数据的 name 属性 因…...

Elasticsearch 单节点安全配置与用户认证
Elasticsearch 单节点安全配置与用户认证 安全扫描时发现了一个高危漏洞:Elasticsearch 未授权访问 。在使用 Elasticsearch 构建搜索引擎或处理大规模数据时,需要启用基本的安全功能来防止未经授权的访问。本文将通过简单的配置步骤,为单节…...

二分查找(带图详解)
优选算法系列 文章目录 优选算法系列前言一、二分查找的思想二、算法使用小总结 三、代码实现四、二分查找拓展4.1、查找第一次出现的target小总结 4.2、target最后出现的位置小总结 五、代码总结 前言 在这篇博客中,我会给大家分享二分查找及其扩展。 这是链接-&…...

【Git】:标签管理
目录 理解标签 创建标签 操作标签 理解标签 标签的作用 标记版本:标签 tag ,可以简单的理解为是对某次 commit 的⼀个标识,相当于起了⼀个别名。例如,在项目发布某个版本的时候,针对最后⼀次 commit 起⼀个 v1.0 这样…...

物品识别 树莓派 5 YOLO v5 v8 v10 11 计算机视觉
0. 要实现的效果 让树莓派可以识别身边的一些物品,比如电脑,鼠标,键盘,杯子,行李箱,双肩包,床,椅子等 1. 硬件设备 树莓派 5 raspberrypi.com/products/raspberry-pi-5/树莓派官方摄…...

单片机软件工程师前景分析
单片机软件工程师的前景在2024年看起来是积极的。随着物联网(IoT)、自动化、智能设备等领域的快速发展,对于能够开发基于单片机(MCU)如STM32、ARM、51等嵌入式系统的软件工程师需求持续增长。这些工程师负责设计和实现…...

在Java中几种常用数据压缩算法的实现及其优劣势
在Java中几种常用数据压缩算法的实现及其优劣势 背景:项目需要引入Redis作为缓存组件,需要考虑到Redis的内存占用(机器内存越大,成本越高),因此需要引入数据压缩。 1、介绍 数据压缩是计算机领域中一项重要…...

Word——如何打出 符号中的 1、2、3等带圆圈的序号
一、方式1 1.1:点击 插入-符号 1.2:字体 选择 Wingdings 或者 Wingdings 2 二、方式2 带1的圈:输入 2460,然后按 AItX 带2的圈:输入 2461,然后按 AItX 带3的圈:输入 2462,然后按 …...

操作系统之进程与线程
进程 定义: 进程是具有独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的独立单位。 组成: 包括程序代码、程序处理的数据、程序计数器、一组寄存器的值以及系统资源(如打开的文件)等。 …...

代码随想录算法训练营打卡第35天:背包问题
前言 zaccheo打卡代码随想录第35天 由于这段时间工作太忙了(加上我的懒病犯了)导致迟打卡了好几天555555.。。。 今天的主要是动态规划中的背包问题,这个真的是蛮难理解的,我把我自己强行按在椅子上半个小时一点一点的看卡哥文章…...

【MySQL】数据库 Navicat 可视化工具与 MySQL 命令行基本操作
💯 欢迎光临清流君的博客小天地,这里是我分享技术与心得的温馨角落 💯 🔥 个人主页:【清流君】🔥 📚 系列专栏: 运动控制 | 决策规划 | 机器人数值优化 📚 🌟始终保持好奇心&…...

vscode(一)安装(ubuntu20.04)
1、更新软件包列表 sudo apt update2、安装依赖包 sudo apt install software-properties-common apt-transport-https wget3、导入Microsoft GPG密钥 wget -q https://packages.microsoft.com/keys/microsoft.asc -O- | sudo apt-key add -4、向系统添加VSCode存储库 sudo…...