Maven学习
前言:
2020年,在部署项目上线时,遇到一些Maven问题,浪费了很多时间,决定系统学习一下每天都在用的Maven。
Maven视频地址
1、Maven之前项目问题
-
一个项目就是一个工程
- 如果项目庞大,需要划分模块
- Maven可以将一个项目拆分多个工程
-
项目中需要的jar包必须手动导入到WEB-INF/ilb目录下
- 同样的jar包出现在不同的项目工程中,浪费存储空间,也让项目臃肿
- maven可以将jar统一保存在“仓库”中,需要使用时,引用即可
-
jar需要别人为我们准备好,或者到官网下载
- 不同技术的官网下载jar不一致
- 下载内容可能存在风险
- Maven以规范的方式下载jar包,第三方工具jar按照规范存放在了Maven中央仓库中
-
一个jar包依赖的其他jar包需要自己手动加入到项目中
FileUpload组件–>IO组件。依赖其他。
2、Maven是什么
-
服务于java平台的自动化构建工具。
Make–>Ant–>Maven–>Gradle
-
构建
- 概念:以“java源文件”、“配置文件”、“jsp”、”HTML“、“图片”等资源为原材料,去生产一个可以运行的项目过程。
- 编译:javac
- 部署:一个动态Web工程运行并不是本身,而是web工程的“编译结果”
- 搭建:
- 概念:以“java源文件”、“配置文件”、“jsp”、”HTML“、“图片”等资源为原材料,去生产一个可以运行的项目过程。
3、Maven运行时环境(rt.jar–>runtime.jar)
4、自动化构建和构建环节
构建过程
- 清理:将之前编译旧的class字节码文件删除
- 编译:javac生产新的class字节码文件
- 测试:自动测试,自动调用junit程序
- 报告:测试程序执行的结构
- 打包:Web工程打war包,java打jar包
- 安装:Maven特点概念—将打包得到文件复制到“仓库”中的制定位置
- 部署:将war包复制到Servlit容器知道目录下,使之可运行
自动化构建
5、Maven核心概念
-
约定的目录结构
原因
- 知道需要的文件的位置
- 自定义框架或工具知道
- 以配置的方式明确告诉框架
- 遵守框架内部已存在的约定
- 约定>配置>编码
-
POM
-
坐标
-
依赖
-
仓库
-
生命周期/插件/目标
-
继承
-
聚合
6、常用Maven命令
-
注意:执行与构建过程相关的Maven命令(编译、测试、打包),必须进入pom.xml所在的目录
-
常用命令
- mvn clean:清理
- mvn compile:编译主程序
- mvn test-compile:编译测试程序
- mvn text:执行测试
- mvn package:打包
- mvn install:安装
- mvn site:生产站点
7、联网问题
- Maven核心程序仅定义了抽象的生命周期,但具体工作必须有特定的插件完成,插件并不属于核心程序。
- 执行Maven插件时,优先在本地仓库查找
- 本地仓库位置:【系统用户加目录】\m2\repository
- Maven核心程序如果不存在需要的插件,那么它会自动连接外网,到中央仓库下载
- 无网络,则构建失败
8、POM
- 概念:Porject Object Model 项目对象模型
- Pom时Maven项目的核心配置文件,与构建过程相关的一切设置都在这个文件中进行配置
9、坐标
-
坐标作用:数学唯一定位一个点
-
Maven坐标(gav)
使用下面三个向量在仓库中唯一定位一个Maven工程
- groupid:公司或者组织名倒叙+项目名
- artifactid:模块名称
- version:版本号
-
maven工程坐标与仓库中路径的对应关系
10、仓库
- 本地仓库:idea设置的目录
- 远程仓库:
- 私服(Nexus):搭建在局域网中,为局域网范围内的所有Mavengc服务
- 中央仓库:架设在Internet上,为全世界所有Maven工程服务
- 中央仓库镜像:为了分担中央仓库流量,提升用户访问速度
- 仓库保存内容:Maven工程
- Maven自身所需要的插件
- 第三方架构或工具类jar包
- 自己开发的Maven工程
11、依赖
自己maven项目的使用
- Maven解析依赖信息时会到本地仓库查找被依赖的jar包
- 自己开发的Maven工程使用Install命令安装后,进入仓库
依赖的范围
- compile:编译
- test:测试 junit
- provided:提供 servlet-api.jar
Maven命令 | 对主程序有效 | 对测试程序有效 | 参与打包 |
---|---|---|---|
compile | 有效 | 有效 | 参与 |
test | 无效 | 有效 | 不参与 |
provided | 有效 | 有效 | 不参与 |
依赖的传递性
- 好处:可以传递的依赖不必在每个模块工程中都重复声明,在"父类"的工程中依赖一次即可
- 注意:非compile范围的无法传递
依赖的排除
-
将通过依赖传递性传入的依赖排除出本工程
-
实现:
-
<exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions>
依赖的原则
- 作用:解决模块之间jar包冲突问题
- 优先级别:jar版本由于依赖冲突时,就近依赖和先声明优先
统一管理依赖版本
-
配置方式
- 使用properties标签内使用自定义标签统一声明版本号
- 在需要统一版本的位置,使用$(自定义标签名)引用声明的版本号
<properties> <java.version>1.8</java.version </properties>
12、生命周期
- 各个构建环节的顺序:按照正确的顺序执行
- Maven核心程序定义了抽象的生命周期,生命周期中各个阶段的具体任务是由插件来完成的
- Maven核心程序,为了更好实现自动化构建,按照这一的特点执行生命周期中的各个阶段:无论现在要执行生命周期的哪一阶段,都要从生命周期最初的位置开始执行。
插件和目标
- 生命周期的各个阶段仅仅定义了要执行的任务是什么
- 各个阶段和目标时对应的
- 相似的目标有特定的插件来完成
依赖范围
生命周期阶段 | 插件目标 | 插件 |
---|---|---|
compile | compile | maven-compiler-plugin |
test-compile | testcompile | maven-compiler-plugin |
依赖的Scope
scope定义了类包在项目的使用阶段。项目阶段包括: 编译,运行,测试和发布。
分类说明
compile
默认scope为compile,表示为当前依赖参与项目的编译、测试和运行阶段,属于强依赖。打包之时,会达到包里去。
test
该依赖仅仅参与测试相关的内容,包括测试用例的编译和执行,比如定性的Junit。
runtime
依赖仅参与运行周期中的使用。一般这种类库都是接口与实现相分离的类库,比如JDBC类库,在编译之时仅依赖相关的接口,在具体的运行之时,才需要具体的mysql、oracle等等数据的驱动程序。
此类的驱动都是为runtime的类库。
provided
该依赖在打包过程中,不需要打进去,这个由运行的环境来提供,比如tomcat或者基础类库等等,事实上,该依赖可以参与编译、测试和运行等周期,与compile等同。区别在于打包阶段进行了exclude操作。
system
使用上与provided相同,不同之处在于该依赖不从maven仓库中提取,而是从本地文件系统中提取,其会参照systemPath的属性进行提取依赖。
import
这个是maven2.0.9版本后出的属性,import只能在dependencyManagement的中使用,能解决maven单继承问题,import依赖关系实际上并不参与限制依赖关系的传递性。
systemPath
当maven依赖本地而非repository中的jar包,sytemPath指明本地jar包路径,例如:
<dependency>
<groupid>org.hamcrest</groupid>
<artifactid>hamcrest-core</artifactid>
<version>1.5</version>
<scope>system</scope>
<systempath>${basedir}/WebContent/WEB-INF/lib/hamcrest-core-1.3.jar</systempath>
</dependency>
dependency中的type
引入某一个依赖时,必须指定type,这是因为用于匹配dependency引用和dependencyManagement部分的最小信息集实际上是{groupId,artifactId,type,classifier}。在很多情况下,这些依赖关系将引用没有classifier的jar依赖。这允许我们将标识设置为{groupId,artifactId},因为type的默认值是jar,并且默认classifier为null。
type的值一般有jar、war、pom等,声明引入的依赖的类型
dependency中的classifier
Classifier可能是最容易被忽略的Maven特性,但它确实非常重要,我们也需要它来帮助规划坐标。设想这样一个情况,有一个jar项目,就说是 dog-cli-1.0.jar 吧,运行它用户就能在命令行上画一只小狗出来。现在用户的要求是希望你能提供一个zip包,里面不仅包含这个可运行的jar,还得包含源代码和文档,换句话说,这是比较正式的分发包。这个文件名应该是怎样的呢?dog-cli-1.0.zip?不够清楚,仅仅从扩展名很难分辨什么是Maven默认生成的构件,什么是额外配置生成分发包。如果能是dog-cli-1.0-dist.zip就最好了。这里的dist就是classifier,默认Maven只生成一个构件,我们称之为主构件,那当我们希望Maven生成其他附属构件的时候,就能用上classifier。常见的classifier还有如dog-cli-1.0-sources.jar表示源码包,dog-cli-1.0-javadoc.jar表示JavaDoc包等等。
classifier它表示在相同版本下针对不同的环境或者jdk使用的jar,如果配置了这个元素,则会将这个元素名在加在最后来查找相应的jar,例如:
jdk17
jdk18
13、继承
统一各个模块工程中junit依赖的版本(依赖传递无法传递test范围的依赖,造成版本不一致)
解决:将junit依赖统一提到“父”工程中,子工程声明junit依赖时不指定版本,以父工程中设定为准,同时也便于修改。
具体实现:
- 创建一个Maven工程作为父工程
- 在子工程中声明对父工程的引用
- 将子工程的坐标与父工程坐标中重复的内容删除
- 在父工程中统一junit的依赖
<dependencyManagement>
<dependencies>
</dependencies>
</dependencyManagement>
- 在子工程中删除junit依赖的版本号部分
14、聚合
问题:继承后,父工程丢失
作用:一键安装各个模块工程
配置方式:在一个总的聚合工程中配置各个参与聚合的模块
<modules>
<module></module>
</modules>
15、web工程自动部署
<build> <build>
<!--工程打包的包名-->
<finalName>test</finalName>
<!--配置构建过程中需要使用到的插件-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--什么时候运行-->
<executions></executions>
</plugin>
</plugins>
</build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin>
</artifactId>
</plugin>
</plugins>
</build>