[TOC]
为什么需要Maven
一个 Java 项目需要确定引入那些依赖包,其次,要确定项目的目录结构,例如 src 存放 java 源码,resource 目录存放配置文件, bin 目录存放编译生成的 .class 文件
此外,海需要配置环境,例如 JDK 的版本,编译打包的流程, 当前代码的版本号
最后,除了使用 Eclips e这样的 IDE 进行编译外,还必须能通过命令行工具进行编译,才能够让项目在一个独立的服务器上编译、测试、部署
Maven 专门为 Java 项目打造的管理和构建工具,主要功能:
- 提供了一套标准化的项目结构
- 提供了一套标准化的构建流程(编译、测试、打包、发布…)
- 提供了一套依赖管理机制
Maven项目结构
核心思想:约定大于配置
目录结构默认:
a-maven-project ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ └── resources │ └── test │ ├── java │ └── resources └── target
a-maven-project
项目的根目录是项目名,有一个项目描述文件 pom.xml
src/main/java
存放 Java 源码的目录
src/main/resources
存放资源文件的目录
src/test/java
存放测试源码的目录
src/test/resources
存放测试资源的目录
target
所有编译、打包生成的文件
项目描述文件 pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>hello</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
...
</properties>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
groupId
类似于 Java 的包名,通常是公司或组织名称
artifactId
类似于 Java 的类名,通常是项目名称
version
版本号
一个 Maven 工程由 groupId,artifactId 和 version 作为唯一标识
例如依赖 commons-logging
1
2
3
4
5
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
使用 <dependency>
声明一个依赖后,Maven就会自动下载这个依赖包并把它放到 classpath 中
setting.xml 元素详解
settings.xml 文件一般存在于 Maven 的安装目录的 conf 子目录下面,或者是用户目录的 .m2子 目录下面
用来设置 Maven 参数的配置文件。并且,settings.xml 是 Maven 的全局配置文件。settings.xml 中包含类似本地仓库、远程仓库和联网使用的代理信息等配置
对于多用户的PC机而言,在 Maven 安装目录的 conf 子目录下面的 settings.xml 才是真正的全局的配置。而用户目录的 .m2 子目录下面的 settings.xml 的配置只是针对当前用户的。当这两个文件同时存在的时候,那么对于相同的配置信息用户目录下面的 settings.xml 中定义的会覆盖 Maven 安装目录下面的 settings.xml 中的定义。用户目录下的 settings.xml 文件一般是不存在的,但是 Maven 允许在这里定义自己的 settings.xml,如果需要在这里定义自己的 settings.xml 的时候就可以把 Maven 安装目录下面的 settings.xml 文件拷贝到用户目录的 .m2 目录下,然后改成自己想要的样子
顶级元素概览
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
</settings>
-
LocalRepository:表示构建系统本地仓库的路径 默认值:~/.m2/repository
<localRepository>${user.home}/.m2/repository</localRepository>
-
InteractiveMode:表示maven是否需要和用户交互以获得输入,默认 true
<interactiveMode>true</interactiveMode>
-
UsePluginRegistry:maven是否需要使用plugin-registry.xml文件来管理插件版本,默认 false
<usePluginRegistry>false</usePluginRegistry>
-
Offline:表示在 Maven 进行项目编译和部署等操作时是否允许 Maven 进行联网来下载所需要的信息。如果构建系统需要在离线模式下运行,则为 true,默认为 false
<offline>false</offline>
-
PluginGroups:在pluginGroups元素下面可以定义一系列的pluginGroup元素。表示当通过plugin的前缀来解析plugin的时候到哪里寻找。pluginGroup元素指定的是plugin的groupId。默认情况下,Maven会自动把org.apache.maven.plugins 和 org.codehaus.mojo 添加到pluginGroups下
1 2 3 4
<pluginGroups> <!--plugin的组织Id(groupId) --> <pluginGroup>org.codehaus.mojo</pluginGroup> </pluginGroups>
-
Servers:作用一般,仓库的下载和部署是在pom.xml文件中的repositories 和 distributionManagement 元素中定义的。然而,一般类似用户名、密码(有些仓库访问是需要安全认证的)等信息不应该在pom.xml文件中配置,这些信息可以配置在 settings.xml 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
<!--配置服务端的一些设置。一些设置如安全证书不应该和pom.xml一起分发。这种类型 的信息应该存在于构建服务器上的settings.xml文件中。 --> <servers> <!--服务器元素包含配置服务器时需要的信息 --> <server> <!--这是server的id(注意不是用户登陆的id),该id与 distributionManagement中repository元素的id相匹配。 --> <id>server001</id> <!--鉴权用户名。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密码。 --> <username>my_login</username> <!--鉴权密码 。鉴权用户名和鉴权密码表示服务器认证所需要的登录名和密 码。密码加密功能已被添加到2.1.0 +。详情请访问密码加密页面 --> <password>my_password</password> <!--鉴权时使用的私钥位置。和前两个元素类似,私钥位置和私钥密码指定了 一个私钥的路径(默认是${user.home}/.ssh/id_dsa)以及如果需要的话, 一个密语。将来passphrase和password元素可能会被提取到外部,但目前它们 必须在settings.xml文件以纯文本的形式声明。 --> <privateKey>${usr.home}/.ssh/id_dsa</privateKey> <!--鉴权时使用的私钥密码。 --> <passphrase>some_passphrase</passphrase> <!--文件被创建时的权限。如果在部署的时候会创建一个仓库文件或者目录, 这时候就可以使用权限(permission)。这两个元素合法的值是一个三位数 字,其对应了unix文件系统的权限,如664,或者775。 --> <filePermissions>664</filePermissions> <!--目录被创建时的权限。 --> <directoryPermissions>775</directoryPermissions> </server> </servers>
-
Mirrors:用于定义一系列的远程仓库的镜像。我们可以在pom中定义一个下载工件的时候所使用的远程仓库。但是有时候这个远程仓库会比较忙,所以这个时候人们就想着给它创建镜像以缓解远程仓库的压力,也就是说会把对远程仓库的请求转换到对其镜像地址的请求。每个远程仓库都会有一个id,这样我们就可以创建自己的mirror来关联到该仓库,那么以后需要从远程仓库下载工件的时候Maven就可以从我们定义好的mirror站点来下载,这可以很好的缓解我们远程仓库的压力。在我们定义的mirror中每个远程仓库都只能有一个mirror与它关联,也就是说你不能同时配置多个mirror的mirrorOf指向同一个repositoryId
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<mirrors> <!-- 给定仓库的下载镜像。 --> <mirror> <!-- 该镜像的唯一标识符。id用来区分不同的mirror元素。 --> <id>mirrorId</id> <!-- 镜像名称 --> <name>PlanetMirror Australia</name> <!-- 该镜像的URL。构建系统会优先考虑使用该URL,而非使用默认的服务器 URL。 --> <url>http://downloads.planetmirror.com/pub/maven2</url> <!-- 被镜像的服务器的id。例如,如果我们要设置了一个Maven中央仓库 (http://repo.maven.apache.org/maven2/)的镜像,就需要将该元素设置 成central。这必须和中央仓库的id central完全一致。 --> <mirrorOf>repositoryId</mirrorOf> </mirror> </mirrors>
-
Proxies:用来配置不同的代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<proxies> <!--代理元素包含配置代理时需要的信息 --> <proxy> <!--代理的唯一定义符,用来区分不同的代理元素。 --> <id>myproxy</id> <!--该代理是否是激活的那个。true则激活代理。当我们声明了一组代理,而 某个时候只需要激活一个代理的时候,该元素就可以派上用处。 --> <active>true</active> <!--代理的协议。 协议://主机名:端口,分隔成离散的元素以方便配置。 --> <protocol>http</protocol> <!--代理的主机名。协议://主机名:端口,分隔成离散的元素以方便配置。 --> <host>proxy.somewhere.com</host> <!--代理的端口。协议://主机名:端口,分隔成离散的元素以方便配置。 --> <port>8080</port> <!--代理的用户名,用户名和密码表示代理服务器认证的登录名和密码。 --> <username>proxyuser</username> <!--代理的密码,用户名和密码表示代理服务器认证的登录名和密码。 --> <password>somepassword</password> <!--不该被代理的主机名列表。该列表的分隔符由代理服务器指定;例子中使 用了竖线分隔符,使用逗号分隔也很常见。 --> <nonProxyHosts>*.google.com|ibiblio.org</nonProxyHosts> </proxy> </proxies>
-
Profiles:根据环境参数来调整构建配置的列表。 settings.xml中的profile元素是pom.xml中profile元素的裁剪版本。它包含了id、activation、repositories、pluginRepositories和 properties元素。这里的profile元素只包含这五个子元素是因为这里只关心构建系统这个整体(这正是settings.xml文件的角色定位),而非单独的项目对象模型设置。如果一个settings.xml中的profile被激活,它的值会覆盖任何其它定义在pom.xml中带有相同id的profile。当所有的约束条件都满足的时候就会激活这个profile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
<profiles> <profile> <!-- profile的唯一标识 --> <id>test</id> <!-- 自动触发profile的条件逻辑 --> <activation> <activeByDefault>false</activeByDefault> <jdk>1.6</jdk> <os> <name>Windows 7</name> <family>Windows</family> <arch>x86</arch> <version>5.1.2600</version> </os> <property> <name>mavenVersion</name> <value>2.0.3</value> </property> <file> <exists>${basedir}/file2.properties</exists> <missing>${basedir}/file1.properties</missing> </file> </activation> <!-- 扩展属性列表 --> <properties /> <!-- 远程仓库列表 --> <repositories /> <!-- 插件仓库列表 --> <pluginRepositories /> ... </profile> </profiles>
<activation>
: 自动触发profile的条件逻辑。这是profile中最重要的元素。跟pom.xml中的profile一样,settings.xml中的profile也可以在特定环境下改变一些值,而这些环境是通过activation元素来指定的。activation元素并不是激活profile的唯一方式。settings.xml文件中的activeProfile元素可以包含profile的id。profile也可以通过在命令行,使用-P标记和逗号分隔的列表来显式的激活(如,-P test)jdk:表示当jdk的版本满足条件的时候激活,在这里是1.6。这里的版本还可以用一个范围来表示,如
<jdk>
[1.4,1.7)</jdk>
表示1.4、1.5和1.6满足;<jdk>
[1.4,1.7]</jdk>
表示1.4、1.5、1.6和1.7满足;os: 表示当操作系统满足条件的时候激活
property: property是键值对的形式,表示当Maven检测到了这样一个键值对的时候就激活该profile
(1)下面的示例表示当存在属性hello的时候激活该profile。
1 2 3
<property> <name>hello</name> </property>
(2)下面的示例表示当属性hello的值为world的时候激活该profile
1 2 3 4
<property> <name>hello</name> <value>world</value> </property>
这个时候如果要激活该profile的话,可以在调用Maven指令的时候加上参数hello并指定其值为world,如:
mvn compile –Dhello=world
file: 表示当文件存在或不存在的时候激活,exists表示存在,missing表示不存在。如下面例子表示当文件hello/world不存在的时候激活该profile
1 2 3 4 5 6 7
<profile> <activation> <file> <missing>hello/world</missing> </file> </activation> </profile>
activeByDefault: 当其值为true的时候表示如果没有其他的profile处于激活状态的时候,该profile将自动被激活
properties: 用于定义属性键值对的。当该profile是激活状态的时候,properties下面指定的属性都可以在pom.xml中使用。对应profile的扩展属性列表
maven属性和ant中的属性一样,可以用来存放一些值。这些值可以在pom.xml中的任何地方使用标记${X}来使用,这里X是指属性的名称。属性有五种不同的形式,并且都能在settings.xml文件中访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<!-- 1、 env.X: 在一个变量前加上"env."的前缀,会返回一个shell环境变量。例如,"env. PATH"指代了$path环境变量(在Windows上是%PATH%)。 2、 project.x:指代了POM中对应的元素值。例如: <project><version>1.0</version></project>通过${project.version}获得version的值。 3、 settings.x: 指代了settings.xml中对应元素的值。例如: <settings><offline>false</offline></settings>通过 ${settings.offline}获得offline的值 4、 Java System Properties: 所有可通过java.lang.System.getProperties()访 问的属性都能在POM中使用该形式访问,例如 ${java.home}。 4. x: 在<properties/>元素中,或者外部文件中设置,以${someVar}的形式使用 --> <properties> <user.install>${user.home}/our-project</user.install> </properties>
如果该profile被激活,则可以在pom.xml中使用${user.install}
repositories: 用于定义远程仓库的,当该profile是激活状态的时候,这里面定义的远程仓库将作为当前pom的远程仓库。它是maven用来填充构建系统本地仓库所使用的一组远程仓库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
<repositories> <!--包含需要连接到远程仓库的信息 --> <repository> <!--远程仓库唯一标识 --> <id>codehausSnapshots</id> <!--远程仓库名称 --> <name>Codehaus Snapshots</name> <!--如何处理远程仓库里发布版本的下载 --> <releases> <!--true或者false表示该仓库是否为下载某种类型构件(发布版,快照版)开启。 --> <enabled>false</enabled> <!--该元素指定更新发生的频率。Maven会比较本地POM和远程POM的时间戳。 这里的选项是:always(一直),daily(默认,每日),interval:X(这里 X是以分钟为单位的时间间隔),或者never(从不)。 --> <updatePolicy>always</updatePolicy> <!--当Maven验证构件校验文件失败时该怎么做-ignore(忽略),fail(失 败),或者warn(警告)。 --> <checksumPolicy>warn</checksumPolicy> </releases> <!--如何处理远程仓库里快照版本的下载。有了releases和snapshots这两组 配置,POM就可以在每个单独的仓库中,为每种类型的构件采取不同的策略。例 如,可能有人会决定只为开发目的开启对快照版本下载的支持。参见 repositories/repository/releases元素 --> <snapshots> <enabled /> <updatePolicy /> <checksumPolicy /> </snapshots> <!--远程仓库URL,按protocol://hostname/path形式 --> <url>http://snapshots.maven.codehaus.org/maven2</url> <!--用于定位和排序构件的仓库布局类型-可以是default(默认)或者legacy (遗留)。Maven 2为其仓库提供了一个默认的布局;然而,Maven 1.x有一种 不同的布局。我们可以使用该元素指定布局是default(默认)还是legacy(遗 留)。 --> <layout>default</layout> </repository> </repositories>
(1) releases、snapshots:这是对于工件的类型的限制 (2) enabled:表示这个仓库是否允许这种类型的工件 (3) updatePolicy:表示多久尝试更新一次。可选值有always、daily、interval:minutes(表示每多久更新一次)和never (4) checksumPolicy:当Maven在部署项目到仓库的时候会连同校验文件一起提交,checksumPolicy表示当这个校验文件缺失或不正确的时候该如何处理,可选项有ignore、fail和warn
pluginRepositories: 在Maven中有两种类型的仓库,一种是存储工件的仓库,另一种就是存储plugin插件的仓库。pluginRepositories的定义和repositories的定义类似,它表示Maven在哪些地方可以找到所需要的插件。和repository类似,只是repository是管理jar包依赖的仓库,pluginRepositories则是管理插件的仓库。maven插件是一种特殊类型的构件。由于这个原因,插件仓库独立于其它仓库。pluginRepositories元素的结构和repositories元素的结构类似。每个pluginRepository元素指定一个Maven可以用来寻找新插件的远程地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
<pluginRepositories> <!-- 包含需要连接到远程插件仓库的信息. 参见profiles/profile/repositories/repository元素的说明 --> <pluginRepository> <releases> <enabled /> <updatePolicy /> <checksumPolicy /> </releases> <snapshots> <enabled /> <updatePolicy /> <checksumPolicy /> </snapshots> <id /> <name /> <url /> <layout /> </pluginRepository> </pluginRepositories>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
<activation> <!--profile默认是否激活的标识 --> <activeByDefault>false</activeByDefault> <!--当匹配的jdk被检测到,profile被激活。例如,1.4激活JDK1.4,1.4.0_2,而!1.4激活所有版本不是以1.4开头的JDK。 --> <jdk>1.5</jdk> <!--当匹配的操作系统属性被检测到,profile被激活。os元素可以定义一些操作系 统相关的属性。 --> <os> <!--激活profile的操作系统的名字 --> <name>Windows XP</name> <!--激活profile的操作系统所属家族(如 'windows') --> <family>Windows</family> <!--激活profile的操作系统体系结构 --> <arch>x86</arch> <!--激活profile的操作系统版本 --> <version>5.1.2600</version> </os> <!--如果Maven检测到某一个属性(其值可以在POM中通过${name}引用),其拥有 对应的name = 值,Profile就会被激活。如果值字段是空的,那么存在属性名称字 段就会激活profile,否则按区分大小写方式匹配属性值字段 --> <property> <!--激活profile的属性的名称 --> <name>mavenVersion</name> <!--激活profile的属性的值 --> <value>2.0.3</value> </property> <!--提供一个文件名,通过检测该文件的存在或不存在来激活profile。missing检 查文件是否存在,如果不存在则激活profile。另一方面,exists则会检查文件是否 存在,如果存在则激活profile。 --> <file> <!--如果指定的文件存在,则激活profile。 --> <exists>${basedir}/file2.properties</exists> <!--如果指定的文件不存在,则激活profile。 --> <missing>${basedir}/file1.properties</missing> </file> </activation>
-
ActiveProfiles: 手动激活profiles的列表,按照profile被应用的顺序定义activeProfile。该元素包含了一组activeProfile元素,每个activeProfile都含有一个profile id。任何在activeProfile中定义的profile id,不论环境设置如何,其对应的 profile都会被激活。如果没有匹配的profile,则什么都不会发生
env-test是一个activeProfile,则在pom.xml(或者profile.xml)中对应id的profile会被激活。如果运行过程中找不到这样一个profile,Maven则会像往常一样运行
1 2 3 4 5 6 7 8 9 10 11
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd"> ... <activeProfiles> <!-- 要激活的profile id --> <activeProfile>env-test</activeProfile> </activeProfiles> ... </settings>
阿里镜像
1
2
3
4
5
6
7
8
9
10
11
<settings>
<mirrors>
<mirror>
<id>aliyun</id>
<name>aliyun</name>
<mirrorOf>central</mirrorOf>
<!-- 国内推荐阿里云的Maven镜像 -->
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
</mirrors>
</settings>
依赖管理
声明了 A 的依赖后, Maven 自动把 aa 都加入项目依赖,不需要自己区研究 A 是否需要依赖 aa
1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.4.2.RELEASE</version>
</dependency>
当声明一个 spring-boot-starter-web 依赖时,Maven 会自动解析并判断最终需要大概二三十个其他依赖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
spring-boot-starter-web
spring-boot-starter
spring-boot
sprint-boot-autoconfigure
spring-boot-starter-logging
logback-classic
logback-core
slf4j-api
jcl-over-slf4j
slf4j-api
jul-to-slf4j
slf4j-api
log4j-over-slf4j
slf4j-api
spring-core
snakeyaml
spring-boot-starter-tomcat
tomcat-embed-core
tomcat-embed-el
tomcat-embed-websocket
tomcat-embed-core
jackson-databind
...
- 依赖关系
Maven 定义了几种依赖关系: compile、test、runtime、provided
scope范围 | 说明 | 示例 |
---|---|---|
compile | 编译时需要用到该jar包(默认) | commons-logging |
test | 编译Test时需要用到该jar包 | junit |
runtime | 编译时不需要,但运行时需要用到 | mysql |
provided | 编译时需要用到,但运行时由JDK或某个服务器提供 | servlet-api |
-
compile是最常用的,Maven会把这种类型的依赖直接放入classpath
- test依赖表示仅在测试时使用,正常运行时并不需要。最常用的test依赖就是JUnit
1 2 3 4 5 6
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.3.2</version> <scope>test</scope> </dependency>
-
runtime依赖表示编译时不需要,但运行时需要。最典型的runtime依赖是JDBC驱动,例如MySQL驱动
1 2 3 4 5 6
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.48</version> <scope>runtime</scope> </dependency>
-
provided依赖表示编译时需要,但运行时不需要。最典型的provided依赖是Servlet API,编译的时候需要,但是运行时,Servlet服务器内置了相关的jar,所以运行期不需要
1 2 3 4 5 6
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0</version> <scope>provided</scope> </dependency>
Maven维护了一个中央仓库(repo1.maven.org),所有第三方库将自身的jar以及相关信息上传至中央仓库,Maven就可以从中央仓库把所需依赖下载到本地
Maven并不会每次都从中央仓库下载jar包。一个jar包一旦被下载过,就会被Maven自动缓存在本地目录(用户主目录的.m2目录),所以,除了第一次编译时因为下载需要时间会比较慢,后续过程因为有本地缓存,并不会重复下载相同的jar包
Maven通过对jar包进行PGP签名确保任何一个jar包一经发布就无法修改。修改已发布jar包的唯一方法是发布一个新版本,因此,某个jar包一旦被Maven下载过,即可永久地安全缓存在本地
-SNAPSHOT结尾的版本号会被Maven视为开发版本,开发版本每次都会重复下载,这种版本只能用于内部私有的Maven repo,公开发布的版本不允许出现SNAPSHOT快照版
构建流程
maven 中包含三个生命周期,每个生命周期包含了多个步骤(phase), goal 则是绑定到 phase 上的,没一个 phase 都对应 1 个或多个 goal,goal 是存在于 maven plugin 中,因此大多数的 maven 功能实际上是存在于插件中,一个 maven 插件提供了一组可以被运行的 goal
phase
以内置的生命周期 default
为例,包含以下 phase:
- validate
- initialize
- generate-sources
- process-sources
- generate-resources
- process-resources
- compile
- process-classes
- generate-test-sources
- process-test-sources
- generate-test-resources
- process-test-resources
- test-compile
- process-test-classes
- test
- prepare-package
- package
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install
- deploy
mvn package
,Maven会执行default生命周期,它会从开始一直运行到package这个phase为止
mvn compile
,Maven也会执行default生命周期,但这次它只会运行到compile
Maven 另一个常用的生命周期是 clean, 执行 3 个 phase: * pre-clean * clean (注意这个clean不是lifecycle而是phase) * post-clean
mvn 命令后面的参数是phase,Maven自动根据生命周期运行到指定的phase
更复杂的例子是指定多个phase,例如,运行 mvn clean package
Maven先执行clean生命周期并运行到clean这个phase,然后执行default生命周期并运行到package这个phase,实际执行的phase如下:
- pre-clean
- clean (注意这个clean是phase)
- validate
- …
- package
goal
执行一个phase又会触发一个或多个goal
-
lifecycle相当于Java的package,它包含一个或多个phase
-
phase相当于Java的class,它包含一个或多个goal
-
goal相当于class的method
Maven命令行
在命令中,进入到 pom.xml
所在目录,成功后可在 target 目录下获得编译后自动打包的jar
1
$ mvn clean package
Maven 命令的格式为 mvn [plugin-name]:[goal-name]
需要了解 《Maven生命周期》
比如:mvn compiler:compile,表示运行 compiler 插件中的 compile goal
可以接受的参数如下:
- -D 指定参数,如 -Dmaven.test.skip=true 跳过单元测试;
- -P 指定 Profile 配置,可以用于区分环境;
- -e 显示maven运行出错的信息;
- -o 离线执行命令,即不去远程仓库更新包;
- -X 显示maven允许的debug信息;
- -U 强制去远程更新snapshot的插件或依赖,默认每天只更新一次
常用命令:
-
创建maven项目:mvn archetype:create 指定 group: -DgroupId=packageName 指定 artifact:-DartifactId=projectName 创建web项目:-DarchetypeArtifactId=maven-archetype-webapp
- 创建maven项目:mvn archetype:generate
- 验证项目是否正确:mvn validate
- maven 打包:mvn package
- 只打jar包:mvn jar:jar
- 生成源码jar包:mvn source:jar
- 产生应用需要的任何额外的源代码:mvn generate-sources
- 编译源代码: mvn compile
- 编译测试代码:mvn test-compile
- 运行测试:mvn test
- 运行检查:mvn verify
- 清理maven项目:mvn clean
- 生成eclipse项目:mvn eclipse:eclipse
- 清理eclipse配置:mvn eclipse:clean
- 生成idea项目:mvn idea:idea
- 安装项目到本地仓库:mvn install
- 发布项目到远程仓库:mvn:deploy
- 在集成测试可以运行的环境中处理和发布包:mvn integration-test
- 显示maven依赖树:mvn dependency:tree
- 显示maven依赖列表:mvn dependency:list
- 下载依赖包的源码:mvn dependency:sources
Maven项目相关命令:
- 启动tomcat:mvn tomcat:run
- 启动jetty:mvn jetty:run
- 运行打包部署:mvn tomcat:deploy
- 撤销部署:mvn tomcat:undeploy
- 启动web应用:mvn tomcat:start
- 停止web应用:mvn tomcat:stop
- 重新部署:mvn tomcat:redeploy
- 部署展开的war文件:mvn war:exploded tomcat:exploded
Maven 多 profile 打包下 -P(Profiles) 参数和 -D(Properties) 参数
-P 参数
在 <profiles>
指定的 <id>
中,可以通过 -P 进行传递和赋值
1
2
3
4
5
6
7
8
9
10
<profiles>
<profile>
<id>prod</id>
...
</profile>
<profile>
<id>test</id>
...
</profile>
</profiles>
mvn clean package -P test
将触发test环境的profile配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<profile>
<id>test</id>
<activation>
<property>
<name>env</name>
<value>test</value>
</property>
</activation>
<id>prod</id>
<property>
<name>env</name>
<value>prod</value>
</property>
</id>
</profile>
-D 参数
1
2
3
<properties>
<attr>defaultattr</attr>
</properties>
mvn -Dattr=newattr clean package
,则pom.xml内attr的实际值将被替换成newattr
使用插件
使用Maven构建项目就是执行lifecycle,执行到指定的phase为止
每个phase会执行自己默认的一个或多个goal
goal是最小任务单元
案例:
mvn compile:
Maven将执行compile这个phase,这个phase会调用compiler插件执行关联的compiler:compile这个goal
执行每个phase,都是通过某个插件(plugin)来执行的,Maven本身其实并不知道如何执行compile,它只是负责找到对应的compiler插件,然后执行默认的compiler:compile这个goal来完成编译
使用Maven,实际上就是配置好需要使用的插件,然后通过phase调用它们
内置的常用标准插件
插件名称 | 对应执行的phase |
---|---|
clean | clean |
compiler | compile |
surefire | test |
jar | package |
自定义插件
使用自定义插件前需要声明
使用maven-shade-plugin可以创建一个可执行的jar,要使用这个插件,需要在pom.xml中声明它
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
...
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
自定义插件往往需要一些配置,例如,maven-shade-plugin需要指定Java程序的入口,它的配置是:
1
2
3
4
5
6
7
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.itranswarp.learnjava.Main</mainClass>
</transformer>
</transformers>
</configuration>
Maven自带的标准插件例如compiler是无需声明的,只有引入其它的插件才需要声明
模块管理
在开发中,把一个大项目分拆为多个模块是降低软件复杂度的有效方法:
对于Maven工程来说,原来是一个大项目:
1
2
3
single-project
├── pom.xml
└── src
拆成 3 个模块后:
1
2
3
4
5
6
7
8
9
10
mutiple-project
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
Maven 可以有效地管理多个模块,把每个模块当作一个独立的Maven项目,它们有各自独立的pom.xml
模块A的pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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.itranswarp.learnjava</groupId>
<artifactId>module-a</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>module-a</name>
...
...
</project>
模块B的pom.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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.itranswarp.learnjava</groupId>
<artifactId>module-b</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>module-b</name>
...
...
</project>
A B 有相同依赖的情况下,可以提取出公共部门作为 parent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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.itranswarp.learnjava</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>parent</name>
<dependencies>
公共依赖
...
...
</dependencies>
</project>
parent的
<packaging>
是pom而不是jar,因为parent本身不含任何Java代码。编写parent的pom.xml只是为了在各个模块中减少重复的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
multiple-project
├── pom.xml
├── parent
│ └── pom.xml
├── module-a
│ ├── pom.xml
│ └── src
├── module-b
│ ├── pom.xml
│ └── src
└── module-c
├── pom.xml
└── src
这样 A B 的依赖可以得到简化,同时需要注意把 parent 的 pom 引入进来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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.itranswarp.learnjava</groupId>
<artifactId>parent</artifactId>
<version>1.0</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<artifactId>module-a</artifactId>
<packaging>jar</packaging>
<name>module-a</name>
</project>
如果 模块A 依赖 模块B,则 模块A 需要 模块B 的jar包才能正常编译,需要在模块A中引入模块B
1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>module-b</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
最后,在编译的时候,需要在根目录创建一个pom.xml统一编译:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>build</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>build</name>
<modules>
<module>parent</module>
<module>module-a</module>
<module>module-b</module>
<module>...</module>
<module>...</module>
</modules>
</project>
在根目录执行mvn clean package时,Maven根据根目录的pom.xml找到包括parent在内的共所有<module>
一次性全部编译