Maven 简介
依赖管理工具
如果说A工程里面用到了B工程的类、接口、配置文件等这样的资源,那么就说A依赖B
构建管理工具
构建:使用原材料生产产品的过程
安装:把一个Maven工程经过打包操作生产的jar包或者war包存入Maven仓库
部署:
jar包:把一个jar包部署到Nexus私服务器上
war包:借助相关Maven插件,例如cargo、Jenkins,将war包部署到Tomcat服务器上
Maven工作机制
1 Maven核心程序下载、解压及配置
Maven的核心配置文件:settings.xml
2 在settings.xml中配置Maven本地仓库
<localRepository>d:\maven-repo</localRepository>
3 配置阿里云提供的镜像仓库
本地没有所需Jar包时需要联网下载,Maven下载Jar包默认为境外的中央仓库,速度较慢,可以修改成阿里云提供的镜像仓库。
修改settings.xml的mirror标签
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
4 配置Maven工程的基础JDK版本
按照默认的配置运行,java工程会使用默认的JDK1.5,因此需要将profile标签复制到settings.xml中修改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>
Maven中的坐标
向量及其取值方式
公司、项目、工程的大小关系: 公司>>项目>>工程
向量的作用:通过三个向量在Maven仓库中唯一地定位到一个jar包,并能根据三个向量规定生成jar包的物理存储路径
①groupId:公司或组织的id(域名的倒序),通常也会加上项目名称
②artifactId:一个项目或者一个项目中一个模块中的id,将来作为Maven工程的工程名
③version:模块的版本号
SNAPSHOT表示快照版本,正在更迭的过程,不稳定的版本
RELEASE表示正式版本
通过坐标对应jar包在本地仓库中的位置
坐标:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
上面坐标在Maven仓库中的存储位置
Maven仓库根目录\javax\servlet\servlet-api\2.5\servlet-api.jar
Maven核心概念:POM
POM:Project Object Model
采用模型化思想,将工程看做一个模型,好利用模型对象的程序对工程进行管理。
Maven核心概念:约定的目录结构
此外主文件目录下的target\classes目录用于存放编译后的工程文件
Maven的实验操作
事前准备
①创建目录作为后面操作的工作空间
D:\maven-workspace
Maven的三个目录:1.Maven核心程序 2.Maven本地仓库 3.Maven工作空间
②在工作目录下mvn archetype:generate
其中mvn是主命令,archetype是插件,generate是目标
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: D:\maven-workspace
[INFO] Parameter: package, Value: com.hikaru.maven
[INFO] Parameter: groupId, Value: com.hikaru.maven
[INFO] Parameter: artifactId, Value: pro01-maven-java
[INFO] Parameter: packageName, Value: com.hikaru.maven
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: D:\maven-workspace\pro01-maven-java
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:53 min
[INFO] Finished at: 2022-04-27T12:14:51+08:00
[INFO] ------------------------------------------------------------------------
③调整
Maven自动生成的工程对Junit的依赖是3.8.1,可以修改为4.1.2
可以删除自动生成的main和test下的java测试文件
④自动生成的pom.xml
标签 | |
project根标签 | 表示对当前工程进行配置、管理 |
modelVision | 从Maven2开始固定为4.0,代表pom.xml采用的标签结构 |
坐标信息 | groupId、artifactId、version表示当前maven工程的坐标信息 |
packing | 当前maven工程的打包方式。默认为'jar'表示java工程,'war'表示web工程,'pom'表示此maven工程是一个管理其他工程的工程 |
depenceies | 存放依赖的坐标信息 + scope表示依赖作用的范围 |
创建Maven版的java工程
要求
运行和Maven操作相关的命令时,必须进入pom.xml所在的目录,否则会报下面的错误
[ERROR] The goal you specified requires a project to execute but there
is no POM in this directory (D:\maven-workspace\space01).
Please verify you invoked Maven from the correct directory. -> [Help 1]
mvn -v和maven构建无关,只要配置Path在任何目录下均可使用
mvn clean
效果:删除target目录
编译操作
mvn compile --> target/classes
mvn test-compile --> target/testclasses
mvn test测试操作
会执行test目录下的测试文件
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.hikaru.maven.CalculatorTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.052 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
target\surefire-reports目录下的txt文件用于存放测试报告
mvn package操作
在maven工程下生成jar包在target目录下
[INFO] Building jar: D:\maven-workspace\space01\pro01-maven-java\target\
pro01-maven-java-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.721 s
[INFO] Finished at: 2022-04-27T15:33:38+08:00
[INFO] ------------------------------------------------------------------------
jar包名为artifactId + version
package操作会先执行测试,测试通过才能打包
mvn install操作
mvn clean install 组合操作,先执行清除target再编译测试安装
[INFO] Installing D:\maven-workspace\space01\pro01-maven-java\target\
pro01-maven-java-1.0-SNAPSHOT.jar to
d:\maven-repo\com\hikaru\maven\pro01-maven-java\1.0-SNAPSHOT\pro01-maven-java-1.0-SNAPSHOT.jar
[INFO] Installing D:\maven-workspace\space01\pro01-maven-java
\pom.xml to
d:\maven-repo\com\hikaru\maven\pro01-maven-java\1.0-SNAPSHOT\pro01-maven-java-1.0-SNAPSHOT.pom
将package的jar包存入本地仓库groupId + artifactId + version目录
将pom.xml改名为jar包同名xml存入相同目录
创建Maven版的web工程
web工程目录结构
生成web工程命令
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.4
下面的操作按照提示执行
编译命令
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) on project pro02-maven-web: Compilation failure: Compilation failure:
[ERROR] /D:/maven-workspace/space01/pro02-maven-web/src/main/java/com/hikaru/maven/servlet/HelloServlet.java:[3,26]
程序包javax.servlet.http不存在
[ERROR] /D:/maven-workspace/space01/pro02-maven-web/src/main/java/com/hikaru/maven/servlet/HelloServlet.java:[4,26] 程序包javax.servlet.http不存在
[ERROR] /D:/maven-workspace/space01/pro02-maven-web/src/main/java/com/hikaru/maven/servlet/HelloServlet.java:[5,26] 程序包javax.servlet.http不存在
[ERROR] /D:/maven-workspace/space01/pro02-maven-web/src/main/java/com/hikaru/maven/servlet/HelloServlet.java:[6,21] 程序包javax.servlet不存在
原因:缺少jar包
Jar包网站:https://mvnrepository.com/
复制依赖写入pom.xml后编译通过:
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ pro02-maven-web ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\maven-workspace\space01\pro02-maven-web\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ pro02-maven-web ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\maven-workspace\space01\pro02-maven-web\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.048 s
[INFO] Finished at: 2022-04-27T16:25:47+08:00
[INFO] ------------------------------------------------------------------------
将war包部署到tomcat webapp目录下
访问时会自动对war包进行解压
让web工程依赖java工程
①将pro01-maven-java下的测试代码CalculatorTest.java复制到目录space01\pro02-maven-web\ src\test\java\com\hikaru\maven下
②在pro02-maven-web工程的pom.xml文件中添加Maven本地库中的pro01-maven-java依赖的坐标信息
<dependency>
<groupId>com.hikaru.maven</groupId>
<artifactId>pro01-maven-java</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
之前对工程pro01-maven-java执行的mvn install命令使其添加到了Maven本地库中
③执行mvn test测试命令,如果测试通过,说明能通过测试调用java工程中的类,则说明web工程依赖到了java工程
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.hikaru.maven.CalculatorTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.027 s - in com.hikaru.maven.CalculatorTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.095 s
[INFO] Finished at: 2022-04-28T10:10:56+08:00
[INFO] ------------------------------------------------------------------------
④mvn package打包
执行mvn package打包命令,测试通过之后在target下生成war包及其解压缩文件,打开后发现WEB-INF\lib目录下存在pro01-maven-java-1.0-SNAPSHOT.jar
测试依赖的范围
1 依赖范围
标签位置:dependencies/dependency/scope
标签的可选值:compile/test/provided/system/runtime/import
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
compile | 有效 | 有效 | 有效 | 有效 |
test | 无效 | 有效 | 有效 | 无效 |
provided | 有效 | 有效 | 有效 | 无效 |
依赖的传递 A->B->C,A->C
结论:compile可以传递,test和provided不能
mvn dependency:list
[INFO]
[INFO] The following files have been resolved:
[INFO] com.hikaru.maven:pro01-maven-java:jar:1.0-SNAPSHOT:compile
[INFO] org.springframework:spring-jcl:jar:5.3.19:compile
[INFO] javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] org.springframework:spring-core:jar:5.3.19:compile
[INFO] junit:junit:jar:4.12:test
mvn dependency:tree
INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ pro02-maven-web ---
[INFO] com.hikaru.maven:pro02-maven-web:war:1.0-SNAPSHOT
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] +- junit:junit:jar:4.12:test
[INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- com.hikaru.maven:pro01-maven-java:jar:1.0-SNAPSHOT:compile
[INFO] \- org.springframework:spring-core:jar:5.3.19:compile
[INFO] \- org.springframework:spring-jcl:jar:5.3.19:compile
[INFO] ------------------------------------------------------------------------
在本地Maven仓库中既存在 org.springframework:spring-core又存在它的依赖
org.springframework:spring-jcl:jar
依赖的排除:阻断依赖的传递
为什么要阻断依赖传递
eg:A->B->D(version 1.0)
A->C->D(version 2.0)
则根据依赖的传递性,A同时拥有两个版本的D,可能会发生冲突
<dependency>
<groupId>com.atguigu.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>
依赖的继承
概念
Maven工程之间,A 工程继承 B 工程
则B工程是父工程
本质上是 A 工程的 pom.xml 中的配置继承了 B 工程中 pom.xml 的配置。
作用
在父工程中统一管理项目中的依赖信息,具体来说是管理依赖信息的版本。
它的背景是:
- 对一个比较大型的项目进行了模块拆分。
- 一个 project 下面,创建了很多个 module。
- 每一个 module 都需要配置自己的依赖信息。
它背后的需求是:
- 在每一个 module 中各自维护各自的依赖信息很容易发生出入,不易统一管理。
- 使用同一个框架内的不同 jar 包,它们应该是同一个版本,所以整个项目中使用的框架版本需要统一。
- 使用框架时所需要的 jar 包组合(或者说依赖信息组合)需要经过长期摸索和反复调试,最终确定一个可用组合。这个耗费很大精力总结出来的方案不应该在新的项目中重新摸索。
通过在父工程中为整个项目维护依赖信息的组合既保证了整个项目使用规范、准确的 jar 包;又能够将以往的经验沉淀下来,节约时间和精力。
3、操作
①创建父工程 pro03-maven-parent
修改打包方式为pom
<packaging>pom</packaging>
只有打包方式为 pom 的 Maven 工程能够管理其他 Maven 工程。打包方式为 pom 的 Maven 工程中不写业务代码,它是专门管理其他 Maven 工程的工程。
②创建模块工程
模块工程类似于 IDEA 中的 module,所以需要进入 pro03-maven-parent 工程的根目录,然后运行 mvn archetype:generate 命令来创建模块工程。
创建模块工程之后,父工程的pom.xml信息添加为:
<modules>
<module>pro04-maven-module</module>
<module>pro05-maven-module</module>
<module>pro06-maven-module</module>
</modules>
③解读子模块的pom.xml
<!-- 使用parent标签指定当前工程的父工程 -->
<parent>
<!-- 父工程的坐标 -->
<groupId>com.atguigu.maven</groupId>
<artifactId>pro03-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!-- 子工程的坐标 -->
<!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
<!-- <groupId>com.atguigu.maven</groupId> -->
<artifactId>pro04-maven-module</artifactId>
<!-- <version>1.0-SNAPSHOT</version> -->
如果子工程坐标中的groupId和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>
⑥在父工程中升级依赖信息的版本
……
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
……
然后在子工程中运行mvn dependency:list,效果如下:
TIP
[INFO] org.springframework:spring-aop:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-core:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-context:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-beans:jar:4.1.4.RELEASE:compile
[INFO] org.springframework:spring-expression:jar:4.1.4.RELEASE:com中声明自定义属性
⑦在父工程中声明自定义属性
<!-- 通过自定义属性,统一指定Spring的版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 自定义标签,维护Spring版本数据 -->
<atguigu.spring.version>4.3.6.RELEASE</atguigu.spring.version>
</properties>
在需要的地方使用 ${} 的形式来引用自定义的属性名:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
真正实现 “一处修改,处处生效” 。
实际意义
编写一套符合要求、开发各种功能都能正常工作的依赖组合并不容易。如果公司里已经有人总结了成熟的组合方案,那么再开发新项目时,如果不使用原有的积累,而是重新摸索,会浪费大量的时间。为了提高效率,我们可以使用工程继承的机制,让成熟的依赖组合方案能够保留下来。
如上图所示,公司级的父工程中管理的就是成熟的依赖组合方案,各个新项目、子系统各取所需即可。
聚合
使用一个“总工程”将各个“模块工程”汇集起来,作为一个整体对应完整的项目。
- 项目:整体
- 模块:部分
TIP
概念的对应关系:
从继承关系角度来看:
- 父工程
- 子工程
从聚合关系角度来看:
- 总工程
- 模块工程
3、好处
- 一键执行 Maven 命令:很多构建命令都可以在“总工程”中一键执行。
以 mvn install 命令为例:Maven 要求有父工程时先安装父工程;有依赖的工程时,先安装被依赖的工程。我们自己考虑这些规则会很麻烦。但是工程聚合之后,在总工程执行 mvn install 可以一键完成安装,而且会自动按照正确的顺序执行。 - 配置聚合之后,各个模块工程会在总工程中展示一个列表,让项目中的各个模块一目了然。
4、聚合的配置
在总工程中配置 modules 即可:
<modules>
<module>pro04-maven-module</module>
<module>pro05-maven-module</module>
<module>pro06-maven-module</module>
</modules>
DependencyManagement
scope import
Maven的继承和Java一样是不支持多继承的,想要使用可以通过DependencyManagement的scope import实现。
如在SpringBoot项目中,我们经常通过在POM文件中继承spring-boot-starter-parent
来导入SpringBoot默认的Jar包,但是在实际开发中往往需要继承多个parnet配置,这时候就可以通过scope import来解决:
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.4</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<!-- SpringCloud需要的依赖 -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<!-- 将 spring-cloud-dependencies 中的依赖全部导入 -->
<scope>import</scope>
</dependency>
<dependency>
<!-- SpringBoot需要的依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>