Maven是一款构建管理、依赖管理、项目管理的工具。Maven提供了开发人员构建一个完整的生命周期框架。开发团队可以自动完成项目的基础工具建设,Maven使用标准的目录结构和默认构建生命周期。
在多个开发团队环境时,Maven可以设置按标准在非常短的时间里完成配置工作。由于大部分项目的设置都很简单,并且可重复使用,Maven让开发人员的工作更轻松,同时创建报表,检查,构建和测试自动化设置。
使用三个“向量”在Maven仓库中定位一个jar包。
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.20version>
对应于本地仓库目录(可看setting.xml中的localRepository标签):
# 文件目录
maven仓库地址/mysql/mysql-connector-java/8.0.20
# 文件结构
.
├── _remote.repositories
├── mysql-connector-java-8.0.20.jar
├── mysql-connector-java-8.0.20.jar.sha1
├── mysql-connector-java-8.0.20.pom
└── mysql-connector-java-8.0.20.pom.sha1
汇总:
命令 | 作用 | 说明 |
---|---|---|
mvn archetype:generate | 命令行创建工程 | 会生成pom文件 |
mvn clean | 清理操作 | 删除target目录 |
mvn compile | 主程序编译 | 编译结果存放的目录:target/classes |
mvn test-compile | 测试程序编译 | 编译结果存放的目录:target/test-classes |
mvn test | 测试操作 | 测试报告存放的目录:target/surefire-reports |
mvn package | 打包 | 存放于target目录, artifactId-version 组成的jar包 |
mvn install | 安装 | 将生成的jar包存入Maven仓库;还会将pom.xml文件转换成xxx.pom一起存入本地仓库。(可以直接使用mvn clean install) |
mvn dependency:tree | 查看依赖树 | 可以看出传递性 |
mvn dependency:list | 查看依赖列表 | |
运行命令要在对应的pom.xml所在目录,操作哪个工程就进入对应工程目录。如果执行的命令的目录没有pom文件会报错如下:
The goal you specified requires a project to execute
but there is no POM in this directory
-D可以带参数执行命令,对插件的目标参数进行配置:
mvn -DpropA=valueA -DpropB=valueB -DpropC=valueC clean package
-DpropertyName=propertyValue
mvn clean install -Dmaven.test.skip=true
-P:指定所用的profile
mvn test -Penv=test
POM:Project Object Model,项目对象模型。和POM类似:DOM,文档对象模型。都是模型化思想的具体体现。
POM表示将工程抽象为一个模型,再用程序中的对象来描述这个模型,便于使用程序管理项目。在开发的过程中,最基本的做法是:将现实生活中的事物抽象成模型,然后封装模型相关的数据作为一个对象,进而可以在程序中计算与现实事物相关的数据(契合 Java中一切皆对象的思想)。
POM理念集中体现在Maven工程根目录下pom.xml这个配置文件中。所以这个pom.xml配置文件就是Maven的核心配置文件。
<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.0modelVersion>
<groupId>com.lfcgroupId>
<artifactId>mallartifactId>
<version>2.1.2-SNAPSHOTversion>
<name>rmallname>
<description>林凡尘的大型门户商城description>
<packaging>pompackaging>
<properties>
<commons-httpclient.version>3.1commons-httpclient.version>
<java.version>1.8java.version>
properties>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
<relativePath/>
parent>
<organization>
<name>Roncooname>
<url>http://www.roncoo.comurl>
organization>
<modules>
<module>ordermodule>
<module>productmodule>
modules>
<distributionManagement>
<repository>
<id>nexus-releasesid>
<name>Nexus Release Repositoryname>
<url>http://192.168.1.221:8081/nexus/content/repositories/releases/url>
repository>
<snapshotRepository>
<id>nexus-snapshotsid>
<name>Nexus Snapshot Repositoryname>
<url>http://192.168.1.221:8081/nexus/content/repositories/snapshots/url>
snapshotRepository>
distributionManagement>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis-spring-boot-starter.version}version>
<scope>compilescope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plusartifactId>
<version>2.1.0version>
dependency>
dependencies>
dependencyManagement>
<developers>
<developer>
<name>Alongname>
<id>shen.jialongid>
<email>[email protected]email>
<roles>
<role>Developerrole>
roles>
<timezone>+8timezone>
developer>
developers>
<build>
<finalName>order-servicefinalName>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
其中父工程的插件管理:
<build>
<pluginManagement>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.6.2version>
plugin>
pluginManagement>
build>
Maven构建过程中的很多默认设定,比如:源文件存放的目录、测试源文件存放的目录、构建输出的目录等,都是经过Maven定义的,定义的位置就是:超级POM。自己创建的POM即使没有指定一个父工程(父POM),本质也默认继承了超级POM,可以类比 自己写的Java类继承了Object类。
内容如下:
<project>
<modelVersion>4.0.0modelVersion>
<repositories>
<repository>
<id>centralid>
<name>Central Repositoryname>
<url>http://repo.maven.apache.org/maven2url>
<layout>defaultlayout>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>centralid>
<name>Central Repositoryname>
<url>http://repo.maven.apache.org/maven2url>
<layout>defaultlayout>
<snapshots>
<enabled>falseenabled>
snapshots>
<releases>
<updatePolicy>neverupdatePolicy>
releases>
pluginRepository>
pluginRepositories>
<build>
<directory>${project.basedir}/targetdirectory>
<outputDirectory>${project.build.directory}/classesoutputDirectory>
<finalName>${project.artifactId}-${project.version}finalName>
<testOutputDirectory>${project.build.directory}/test-classestestOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/javasourceDirectory>
<scriptSourceDirectory>src/main/scriptsscriptSourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/javatestSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resourcesdirectory>
resource>
resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resourcesdirectory>
testResource>
testResources>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-antrun-pluginartifactId>
<version>1.3version>
plugin>
<plugin>
<artifactId>maven-assembly-pluginartifactId>
<version>2.2-beta-5version>
plugin>
<plugin>
<artifactId>maven-dependency-pluginartifactId>
<version>2.1version>
plugin>
<plugin>
<artifactId>maven-release-pluginartifactId>
<version>2.0version>
plugin>
plugins>
pluginManagement>
build>
<reporting>
<outputDirectory>${project.build.directory}/siteoutputDirectory>
reporting>
<profiles>
<profile>
<id>release-profileid>
<activation>
<property>
<name>performReleasename>
<value>truevalue>
property>
activation>
<build>
<plugins>
<plugin>
<inherited>trueinherited>
<artifactId>maven-source-pluginartifactId>
<executions>
<execution>
<id>attach-sourcesid>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
<plugin>
<inherited>trueinherited>
<artifactId>maven-javadoc-pluginartifactId>
<executions>
<execution>
<id>attach-javadocsid>
<goals>
<goal>jargoal>
goals>
execution>
executions>
plugin>
<plugin>
<inherited>trueinherited>
<artifactId>maven-deploy-pluginartifactId>
<configuration>
<updateReleaseInfo>trueupdateReleaseInfo>
configuration>
plugin>
plugins>
build>
profile>
profiles>
project>
和Java类一样,POM之间是单继承的,当我们给一个POM指定了父POM,那么继承关系如下图所示:
在POM的继承关系中,子POM可以覆盖父POM中的配置;如果子POM没有覆盖,那么父POM中的配置将会被继承。按照这个规则,继承关系中的所有POM叠加到一起,就得到了最终生效的POM。显然Maven实际运行过程中,执行构建操作就是按照这个最终生效的POM来运行的。使用mvn help:effective-pom
来查看。
目标 | 说明 |
---|---|
help:active-profiles | 列出当前已激活的profile |
help:all-profiles | 列出当前工程所有可用的profile |
help:describe | 描述一个插件的Mojo属性 |
help:effective-pom | 以XML格式展示有效的POM |
help:effective-settings | 为当前工程以XML格式计算得到的settings配置 |
help:evaluate | 计算用户在交互模式下给出的Maven表达式,查看pom中property的属性,如有变量则会显示计算后的值。执行命令之后输入:${property_xxx} |
help:system | 显示平台详细信息列表,如系统属性和环境变量 |
具体有哪些系统属性可以在程序中通过 System.getProperties()
获取。在命令可以通过以下方式获取:
在POM中配置的元素值,可以通过${project.xxx}
访问。
访问maven的配置文件setting.xml中配置的元素值
${setting.标签名}
我们配置的build标签都是对超级POM配置的叠加,并且在默认配置无法满足需求的时候会定制构建过程。
定义约定的目录结构
目录名 | 作用 |
---|---|
sourceDirectory | 主体源程序存放目录 |
scriptSourceDirectory | 脚本源程序存放目录 |
testSourceDirectory | 测试源程序存放目录 |
outputDirectory | 主体源程序编译结果输出目录 |
testOutputDirectory | 测试源程序编译结果输出目录 |
resources | 主体资源文件存放目录 |
testResources | 测试源文件存放目录 |
directory | 构建结果输出目录 |
备用插件管理
通过pluginManagement 标签管理起来的插件就像dependencyManagement一样,子工程使用时可以省略版本号,起到腐工程中统一管理版本的效果。
dependencyManagement 标签存放着几个极少用到的插件:
生命周期插件
plugins标签存放的是默认生命周期中实际会用到的插件。
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
<skipTests>trueskipTests>
<verbose>trueverbose>
<showWarnings>trueshowWarnings>
<fork>truefork>
<executable>executable>
<compilerVersion>1.3compilerVersion>
<meminitial>128mmeminitial>
<maxmem>512mmaxmem>
<compilerArgument>-verbose -bootclasspath ${java.home}\lib\rt.jarcompilerArgument>
configuration>
<executions>
<execution>
<id>default-testCompileid>
<phase>test-compilephase>
<goals>
<goal>testCompilegoal>
goals>
execution>
executions>
plugin>
坐标部分:
groupId和artifactId标签定义了插件的坐标,作为Maven的自带插件这里省略了groupId。
执行部分:
executions标签内可以配置多个execution,execution标签内:
configuration标签内进行配置使用时的标签是插件本身定义的。
每个插件能够做哪些设置都是各个插件自己规定的,无法一概而论。
提出问题
在本地环境通过setting.xml实现的配置的JDK版本,将Maven配置上服务器,会脱离setting.xml,该如何保证程序的运行?可以将JDK的版本信息,配置到负责编译的maven-compiler-plugin插件,让它在构建的过程中,按照我们指定的信息工作。如上文所示。
两种配置的区别:
需求
spring-boot-maven-plugin并不是Maven自带的插件,而是SpringBoot提供的,用来改变Maven的打包行为,默认情况下Maven调用的是maven-jar-plugin插件的jar目标,生成可运行的jar包。
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.5.5version>
plugin>
plugins>
build>
插件目标:
插件目标 | 说明 |
---|---|
spring-boot:repackage | 默认goal。在mvn package之后,再次打包可执行的jar/war,同时保留mvn package生成的jar/war为.origin |
spring-boot:run | 运行Spring Boot应用 |
spring-boot:start | 在mvn integration-test阶段,进行Spring Boot应用生命周期的管理 |
spring-boot:stop | 在mvn integration-test阶段,进行Spring Boot应用生命周期的管理 |
spring-boot:build-info | 生成Actuator使用的构建信息文件build-info.properties |
从外部视角来看,profile可以在下面两种配置文件中配置:
从内部视角来看,配置profile有如下语法要求:
语法要求 | 说明 |
---|---|
profiles/profile标签 |
|
id标签 | 每个profile都必须有一个id标签,指定该profile的唯一标识 。这个id标签的值会在命令行调用profile时被用到,这个命令格式是:-D |
其他允许出现的标签 | 一个profile可以覆盖项目的最终名称、项目依赖、插件配置等各个方面以影响构建行为。
|
列出所有激活的profile
mvn help:active-profiles
指定某个具体的profile
mvn compile -P
Maven为了能够通过profile实现各不同运行环境切换,提供了一种【资源属性过滤】的机制。通过属性替换实现不同环境使用不同参数。
标签位置:dependencies/dependency/scope
可选值:compile / test / provided / system / runtime / import
compile 和 test对比
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
compile | 有效 | 有效 | 有效 | 有效 |
test | 无效 | 有效 | 有效 | 无效 |
说明:
PS:
compile 和 provided对比
main目录(空间) | test目录(空间) | 开发过程(时间) | 部署到服务器(时间) | |
---|---|---|---|---|
compile | 有效 | 有效 | 有效 | 有效 |
provided | 有效 | 有效 | 有效 | 无效 |
结论
概念:
A依赖B,B依赖C,那么在A没有配置对C的依赖的情况下,A里面能不能直接使用C?
传递原则:
在A依赖B,B依赖C的前提下,C是否能传递到A,取决于B依赖C时设置的依赖范围。
概念:
当A依赖B,B依赖C而且C可以传递到A的时候,A不想要C,需要在A里面把C排除掉,而往往这种情况都是为了避免jar包之间的冲突。
所以配置以来的排除其实就是阻止某些jar包的传递。
配置方式:
<dependency>
<groupId>com.lfcgroupId>
<artifactId>commonartifactId>
<version>2.1.0version>
<exclusions>
<exclusion>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
exclusion>
exclusions>
dependency>
概念:
Maven工程之间,A工程集成B工程
B:父工程
A:子工程
本质上是A工程的pom.xml中配置继承了B工程中pom.xml的配置。
作用:
在父工程中统一管理项目中的依赖信息,具体来说是管理依赖信息的版本。简言之:父工程管理依赖,子工程各取所需。
它的背景:
背后的需求:
注意:
只有打包方式为pom的 Maven工程才能够管理其他的Maven工程,打包方式为pom的Maven工程中不写业务代码,它是专门管理其他Maven工程的工程。
实际意义:
编写一套符合要求,开发各种功能都能正常工作的雨来组合并不容易。如果公司里有人总结了成熟的方案,在开发新项目时,如果不使用原有的积累,而是重新摸索,会浪费大量的时间。为了提高效率,我们可以使用工程继承的机制,让成熟的依赖组合方案能够保留下来。如图所示,公司级的父工程中管理的就是成熟的依赖组合方案,各个新项目、子系统各取所需即可。
概念:
使用一个“总工程”将各个“模块工程”汇集成一个整体对应完整的项目。
从集成关系角度:
从聚合关系角度:
目的:
聚合的位置:
<modules>
<module>ordermodule>
<module>productmodule>
<module>paymodule>
<modules>
依赖循环问题:
如果A工程以来B工程,B工程以来C工程,C工程反过来又依赖A工程。那么在执行构建时会报错如下:
[ERROR][ERROR] The projects in the reator contains a cyclic reference
├── pom.xml
└── src # 源码目录
└── main # 主体程序目录
├── java # Java源代码
│ └── com # package目录
└── resources # 配置文件
├── test # 单元测试目录
├── java # Java源代码
└── com # package目录
另外还有一个target目录存放构建输出的结果(class字节码文件、jar包、war包等)。
Maven为了让构建过程尽可能自动化完成,所以必须约定目录结构。比如进行编译操作:先去java源程序目录读取Java源代码,然后执行编译,最后吧编译的结果存放到target中。
为了让构建过程自动化完成,Maven设定了三个生命周期,生命周期中的每一个环节对应构建过程中的一个操作。
生命周期 | 作用 | 环节 |
---|---|---|
Clean | 清理相关操作 | pre-clean clean post-clean |
Site | 生成相关站点 | pre-site site post-site deploy-site |
Default | 主要构建过程 | validate compile test package verify install site deploy |
Maven的核心程序负责宏观调度,不做具体工作。具体工作都是由Maven插件完成的。例如:编译就是由maven-compiler-plugin-3.1.jar插件来完成的。
一个插件可以对应多个目标,而每一个目标都和生命周期中的某一环节对应。
Default生命周期中有compile和test-compile两个和编译相关的环节,这两个环节对应compile和test-compile两个目标,而这两个目标都是由maven-compiler-plugin-3.1.jar插件来执行的。
仓库类型 | 说明 |
---|---|
proxy | 某个远程仓库的代理 |
group | 存放:通过Nexus获取的第三方jar包 |
hosted | 存放:本团队其他开发人员 部署到Nexus 的jar包 |
仓库名称 | 说明 |
---|---|
maven-central | Nexus对Maven中央仓库的代理 |
maven-public | Nexus默认创建,供开发人员下载使用的组仓库 |
maven-release | Nexus默认创建,供开发人员部署自己jar包的宿主仓库,要求release版本 |
maven-snapshots | Nexus默认创建,供开发人员部署自己jar包的宿主仓库,要求snapshots版本 |
<distributionManagement>
<repository>
<id>nexusid>
<name>nexus RELEASEname>
<url>http://127.0.0.1:8089/nexus/repository/maven_release/url>
repository>
<snapshotRepository>
<id>nexusid>
<name>nexus SNAPSHOTname>
<url>http://127.0.0.1:8089/nexus/repository/maven_snapshot/url>
snapshotRepository>
distributionManagement>
执行
mvn deploy
<repositories>
<repository>
<id>maven_snapshotid>
<url>http://127.0.0.1:8089/nexus/repository/maven_snapshoturl>
<releases>
<enabled>trueenabled>
releases>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
在命令行cd 到子模块的目录(含pom.xml的目录)再执行mvn clean package
,即可打包子模块。
mvn clean install -pl 单模块名 -am -Dmaven.test.skip=true
指令名 | 说明 |
---|---|
-pl | 打包指定模块,以逗号分隔 |
-am | 打包所指定模块的依赖模块 |
-amd | 打包所指定模块的依赖模块的依赖,含有传递依赖 |
-rf | 按指定顺序开始打包 |
基本思路:
这个插件是IDEA插件,不是Maven插件。它能够给我们罗列出来同一个jar包的不同版本,以及他们的来源。但是对不同的jar包中同名的类没有办法。
使用Maven的enforcer插件即可以检测同一个jar包的不同版本,又可以检测不同jar包中的同名的类。
实际开发过程中用到的一些jar包并非用Maven的方式发布,自然也没法通过Maven导入。比如人脸识别通用的jar包、银行sdk的jar包、海康威视jar包等。
mvn install:install-file -Dfile=[jar包路径] \
-DgroupId=[给该jar包 强行设定坐标groupId] \
-DartifactId=[给该jar包 强行设定坐标artifactI] \
-Dversion=1 \
-Dpackage=jar
实际工作中,主要掌握以下几种操作:
参考:
尚硅谷的封捷老师:https://www.bilibili.com/video/BV12q4y147e4?p=1