四、基于IDEA进行Maven工程构建
4.1 构建概念和构建过程
项目构建是指将源代码、依赖库和资源文件等转换成可执行或可部署的应用程序的过程,在这个过程中包括编译源代码、链接依赖库、打包和部署等多个步骤。
常见的构建工具包括 Maven、Gradle、Ant等。
4.2 命令方式项目构建
| 命令 | 描述 |
|---|---|
| mvn compile | 编译项目,生成target文件 |
| mvn package | 项目打包,在target文件夹中生成war/jar文件 |
| mvn clean | 清理编译或打包后的项目文件,删除target文件夹 |
| mvn install | 打包后上传到maven本地仓库(本地部署) |
| mvn deploy | 只打包,上传到maven私服仓库(私服部署) |
| mvn site | 生成站点 (报告) |
| mvn test | 执行测试源码 (测试) |
maven自带打包插件和jdk版本不匹配:pom.xml 添加以下代码即可
<build>
<!-- jdk17和打war包和site插件版本插件不匹配 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.9.1</version> <!-- 此处替换为你所需的版本号 -->
</plugin>
</plugins>
</build>
命令触发练习:
mvn 命令
#清理
mvn clean
#清理,并重新打包
mvn clean package
#执行测试代码
mvn test
4.3 可视化方式项目构建
4.4 构建插件、命令、生命周期命令之间关系
-
构建生命周期
我们发现一个情况!当我们执行package命令也会自动执行compile命令!
[INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ mybatis-base-curd --- [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ mybatis-base-curd --- [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ mybatis-base-curd --- [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ mybatis-base-curd --- [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ mybatis-base-curd --- [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ mybatis-base-curd --- [INFO] Building jar: D:\javaprojects\backend-engineering\part03-mybatis\mybatis-base-curd\target\mybatis-base-curd-1.0-SNAPSHOT.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5.013 s [INFO] Finished at: 2023-06-05T10:03:47+08:00 [INFO] ------------------------------------------------------------------------这种行为就是因为构建生命周期产生的!构建生命周期可以理解成是一组固定构建命令的有序集合,触发周期后的命令,会自动触发周期前的命令!!!
构建周期作用:会简化构建过程
例如:项目打包 mvn clean package即可。
主要三个构建生命周期:
-
清理周期(Clean Lifecycle):主要是对项目编译生成文件进行清理
包含命令:clean
-
默认周期(Default Lifecycle):定义了真正构件时所需要执行的所有步骤,它是生命周期中最核心的部分
包含命令:compile - test - package - install / deploy
-
报告周期(Site Lifecycle)
包含命令:site
打包: mvn clean package 本地仓库: mvn clean install
-
-
插件、命令、周期三者关系(了解)
周期→包含若干命令→包含若干插件
使用周期命令构建,简化构建过程!
最终进行构建的是插件!
五、基于IDEA 进行Maven依赖管理
5.1 依赖管理概念
我们通过定义 POM 文件,Maven 能够自动解析项目的依赖关系,并通过 Maven 仓库自动下载和管理依赖,从而避免了手动下载和管理依赖的繁琐工作和可能引发的版本冲突问题。
5.2 Maven工程核心信息配置和解读(GAVP)
位置:pom.xml
<!-- 模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<groupId>com.companyname.project-group</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>project</artifactId>
<!-- 版本号 -->
<version>1.0.0</version>
<!--打包方式
默认:jar
jar指的是普通的java项目打包方式! 项目打成jar包!
war指的是web项目打包方式!项目打成war包!
pom不会讲项目打包!这个项目作为父工程,被其他工程聚合或者继承!后面会讲解两个概念
-->
<packaging>jar/pom/war</packaging>
5.3 Maven工程依赖管理配置
位置:pom.xml
依赖管理和依赖添加
<!--
通过编写依赖jar包的gav必要属性,引入第三方依赖!
scope属性是可选的,可以指定依赖生效范围!
依赖信息查询方式:
1. maven仓库信息官网 https://mvnrepository.com/
2. mavensearch插件搜索
-->
<dependencies>
<!-- 引入具体的依赖包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<!--
生效范围
- compile :main目录 test目录 运行打包 [默认]
- provided:main目录 test目录 Servlet
- runtime: 打包运行 MySQL
- test: test目录 junit
-->
<scope>runtime</scope>
</dependency>
</dependencies>
依赖版本统一提取和维护
<!--声明版本-->
<properties>
<!--命名随便,内部制定版本号即可!-->
<junit.version>4.11</junit.version>
<!-- 也可以通过 maven规定的固定的key,配置maven的参数!如下配置编码格式!-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<!--引用properties声明版本 -->
<version>${junit.version}</version>
</dependency>
</dependencies>
5.4 Maven工程依赖下载失败错误解决(重点)
在使用 Maven 构建项目时,可能会发生依赖项下载错误的情况,主要原因有以下几种:
- 下载依赖时出现网络故障或仓库服务器宕机等原因,导致无法连接至 Maven 仓库,从而无法下载依赖。
- 依赖项的版本号或配置文件中的版本号错误,或者依赖项没有正确定义,导致 Maven 下载的依赖项与实际需要的不一致,从而引发错误。
- 本地 Maven 仓库或缓存被污染或损坏,导致 Maven 无法正确地使用现有的依赖项。
解决方案:
-
检查网络连接和 Maven 仓库服务器状态。
-
确保依赖项的版本号与项目对应的版本号匹配,并检查 POM 文件中的依赖项是否正确。
-
清除本地 Maven 仓库缓存(lastUpdated 文件),因为只要存在lastupdated缓存文件,刷新也不会重新下载。本地仓库中,根据依赖的gav属性依次向下查找文件夹,最终删除内部的文件,刷新重新下载即可!
例如: pom.xml依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency>文件:
5.5 Maven工程Build构建配置
项目构建是指将源代码、依赖库和资源文件等转换成可执行或可部署的应用程序的过程,在这个过程中包括编译源代码、链接依赖库、打包和部署等多个步骤。
默认情况下,构建不需要额外配置,都有对应的缺省配置。当然了,我们也可以在pom.xml定制一些配置,来修改默认构建的行为和产物!
例如:
- 指定构建打包文件的名称,非默认名称
- 制定构建打包时,指定包含文件格式和排除文件
- 打包插件版本过低,配置更高版本插件
构建配置是在pom.xml / build标签中指定!
指定打包命名
<!-- 默认的打包名称:artifactid+verson.打包方式 -->
<build>
<finalName>定义打包名称</finalName>
</build>
指定打包文件
如果在java文件夹中添加java类,会自动打包编译到classes文件夹下!
但是在java文件夹中添加xml文件,默认不会被打包!
默认情况下,按照maven工程结构放置的文件会默认被编译和打包!
除此之外、我们可以使用resources标签,指定要打包资源的文件夹要把哪些静态资源打包到 classes根目录下!
<!--打包指定静态资源-->
<build>
<resources>
<resource>
<!-- 指定要打包资源的文件夹 要把哪些静态资源打包到 classes根目录下-->
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>spring/*</exclude>
</excludes>
<includes>
<include>*.xml</include>
<!--<include>*/*.properties</include>-->
</includes>
</resource>
</resources>
</build>
配置依赖插件
在 Maven 中,插件和依赖项是两个不同的概念。
依赖是指项目依赖的库或其他项目,Maven 使用依赖机制来自动管理项目的依赖关系!
插件是一种用于扩展 Maven 构建过程的工具。Maven 中有很多插件可以用来完成各种任务,例如编译、测试、打包、发布等等。
总体而言,依赖是项目所依赖的基本组件,而插件是用于扩展 Maven 构建过程的工具。在 pom.xml 文件中,依赖通常包含在 <dependencies> 元素中,而插件通常包含在 <plugins> 元素中。
常用的插件:修改构建插件版本、修改jdk版本、tomcat插件、mybatis逆向工程插件等等!
<build>
<!-- jdk17 和 war包版本插件不匹配 -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
</plugins>
</build>
六、Maven依赖传递和依赖冲突
6.1 Maven依赖传递特性
假如有Maven项目A,项目B依赖A,项目C依赖B。那么我们可以说 C依赖A。也就是说,依赖的关系为:C—>B—>A, 那么我们执行项目C时,会自动把B、A都下载导入到C项目的jar包文件夹中,这就是依赖的传递性。
传递的原则
在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围。
- B 依赖 C 时使用 compile 范围:可以传递
- B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。
作用
- 简化依赖导入过程
- 确保依赖版本正确
依赖传递终止
- 非compile范围进行依赖传递
- 依赖冲突(传递的依赖已经存在)
案例:导入jackson依赖
分析:jackson需要三个依赖
依赖传递关系:data-bind中,依赖其他两个依赖
最佳导入:直接可以导入data-bind,自动依赖传递需要的依赖
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
6.2 Maven依赖冲突特性
当直接引用或者间接引用出现了相同的jar包! 这时呢,一个项目就会出现相同的重复jar包,这就算作冲突!依赖冲突避免出现重复依赖,并且终止依赖传递!
maven自动解决依赖冲突问题能力,会按照自己的原则,进行重复依赖选择。同时也提供了手动解决的冲突的方式,不过不推荐!
解决依赖冲突(如何选择重复依赖)方式:
-
自动选择原则
-
短路优先原则(第一原则)
A—>B—>C—>D—>E—>X(version 0.0.1)
A—>F—>X(version 0.0.2)
则A依赖于X(version 0.0.2)。
-
依赖路径长度相同情况下,则“先声明优先”(第二原则)
A—>E—>X(version 0.0.1)
A—>F—>X(version 0.0.2)
在<depencies></depencies>中,先声明的,路径相同,会优先选择!
-
-
手动排除
<dependency> <groupId>com.mytest.maven</groupId> <artifactId>pro01-maven-java</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> <!-- 使用excludes标签配置依赖的排除 --> <exclusions> <!-- 在exclude标签中配置一个具体的排除 --> <exclusion> <!-- 指定要排除的依赖的坐标(不需要写version) --> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> -
小案例
伪代码如下:
前提: A 1.1 -> B 1.1 -> C 1.1 F 2.2 -> B 2.2 pom声明: F 2.2 A 1.1 B 2.2请问最终会导入哪些依赖和对应版本?
七、Maven工程继承和聚合关系
7.1 Maven工程继承关系
-
继承概念
Maven 继承是指在 Maven 的项目中,让一个项目从另一个项目中继承配置信息的机制。继承可以让我们在多个项目中共享同一配置信息,简化项目的管理和维护工作。
-
继承作用
在父工程中统一管理项目中的依赖信息。
它的背景是:
- 对一个比较大型的项目进行了模块拆分。
- 一个 project 下面,创建了很多个 module。
- 每一个 module 都需要配置自己的依赖信息。 它背后的需求是:
- 多个模块要使用同一个框架,它们应该是同一个版本,项目中使用的框架版本需要统一管理。
- 使用框架时所需要的 jar 包组合(或者说依赖信息组合)需要经过长期摸索和反复调试,最终确定一个可用组合。这个耗费很大精力总结出来的方案不应该在新的项目中重新摸索。 通过在父工程中为整个项目维护依赖信息的组合既保证了整个项目使用规范、准确的 jar 包;又能够将以往的经验沉淀下来,节约时间和精力。
-
继承语法
- 父工程
<groupId>com.mytest.maven</groupId> <artifactId>pro03-maven-parent</artifactId> <version>1.0-SNAPSHOT</version> <!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
pom
- 子工程 ```xml <!-- 使用parent标签指定当前工程的父工程 --> <parent> <!-- 父工程的坐标 --> <groupId>com.mytest.maven</groupId> <artifactId>pro03-maven-parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <!-- 子工程的坐标 --> <!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 --> <!-- <groupId>com.mytest.maven</groupId> --> <artifactId>pro04-maven-module</artifactId> <!-- <version>1.0-SNAPSHOT</version> --> ``` - 父工程
-
父工程依赖统一管理 (推荐)
- 父工程声明版本
<!-- 使用dependencyManagement标签配置对依赖的管理 --> <!-- 被管理的依赖并没有真正被引入到工程 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.0.0.RELEASE</version> </dependency> </dependencies> </dependencyManagement> - 子工程引用版本
<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。 --> <!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 --> <!-- 具体来说是由父工程的dependencyManagement来决定。 --> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> </dependency> </dependencies>
- 父工程声明版本
7.2 Maven工程聚合关系
-
聚合概念
Maven 聚合是指将多个项目组织到一个父级项目中,以便一起构建和管理的机制。聚合可以帮助我们更好地管理一组相关的子项目,同时简化它们的构建和部署过程。
-
聚合作用
- 管理多个子项目:通过聚合,可以将多个子项目组织在一起,方便管理和维护。
- 构建和发布一组相关的项目:通过聚合,可以在一个命令中构建和发布多个相关的项目,简化了部署和维护工作。
- 优化构建顺序:通过聚合,可以对多个项目进行顺序控制,避免出现构建依赖混乱导致构建失败的情况。
- 统一管理依赖项:通过聚合,可以在父项目中管理公共依赖项和插件,避免重复定义。
-
聚合语法
父项目中包含的子项目列表。
<project> <groupId>com.example</groupId> <artifactId>parent-project</artifactId> <packaging>pom</packaging> <version>1.0.0</version> <modules> <module>child-project1</module> <module>child-project2</module> </modules> </project> -
聚合演示
通过触发父工程构建命令、引发所有子模块构建!产生反应堆!
八、了解Maven仓库之间的关系和优先级
Maven 仓库主要分为本地仓库(Local Repository)、中央仓库(Central Repository)和远程仓库(Remote Repository)三种类型。
-
本地仓库
本地仓库是 Maven 在本地系统上创建的一个目录,用于存储本地构建的项目所需的所有依赖项和插件等。每个用户都有一个本地仓库,位于用户主目录下的 .m2 文件夹中。当 Maven 需要某个依赖项时,它会首先查找本地仓库中是否已经存在该依赖项的本地缓存,如果存在则直接使用,否则会向远程仓库请求下载。
-
中央仓库
中央仓库是 Maven 官方维护的仓库,包含了大量的开源项目和库的依赖项和插件等,是 Maven 构建时默认会访问的仓库。当本地仓库中不存在所需依赖项时,Maven 将尝试从中央仓库下载依赖项并缓存到本地仓库中。Maven 默认使用的是 Maven 官方的中央仓库,也可以使用其他公共或私有的中央仓库。
-
远程仓库
远程仓库通常由管理员或团队维护,用于共享内部项目的构件和依赖,或者作为第三方依赖库的备选源。Maven 会检查远程仓库中是否存在所需的依赖项,如果存在则下载并缓存到本地仓库中。远程仓库可以是私有的或公共的,也可以是 Maven 的其他仓库类型,如镜像仓库。
仓库的优先级顺序为:本地仓库 > 远程仓库 > 中央仓库。也就是说,如果本地仓库中已经存在所需依赖项,Maven 将直接使用本地仓库中的该依赖项,而不会再去远程仓库或中央仓库下载。
总之,Maven 的仓库机制提供了依赖项和插件管理的核心支持,使得项目的构建和部署更加便捷和高效。理解和掌握 Maven 的仓库机制对于 Maven 的使用和开发至关重要。