Maven 是 Apache 开源的跨平台项目管理工具,主要服务基于 Java 平台的项目构建、依赖管理和项目信息管理等,类似于 Linux 的 yum。其 POM(Project Object Model)文件是 Maven 最核心的配置文件。
Maven 项目骨架:
根目录:工程名
|---src:源码
|---|---main:主程序
|---|---|---java:主程序代码路径
|---|---|---resource:主程序配置文件路径
|---|---test:测试
|---|---|---java:测试代码路径
|---|---|---resource:测试配置文件路径
|---pom.xml:maven 配置文件
Maven 坐标与依赖
Maven 坐标可以类比数学中平面几何的坐标,任何一个坐标都能唯一标识平面中的一个点。这个点在 Maven 中就是对应的 .jar、.war 等文件。Maven 针对每一个坐标文件提供了多个属性配置来构建自己的坐标,只要能提供正确配置的坐标元素,Maven 就能找到对应的坐标文件。基本坐标元素如下:
- groupId:定义当前 Maven 项目隶属的实际项目。
- artifactId:定义实际项目中的一个 Maven 项目(模块)。
- version:定义 Maven 项目当前所处的版本。
- packaging:定义 Maven 项目打包方式。jar、war、pom。默认为 jar。
<dependencies>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<classifier></classifier>
<exclusions>
<exclusion>
<groupId></groupId>
<artifactId></artifactId>
</exclusion>
......
</exclusions>
<optional></optional>
<scope></scope>
<type></type>
</dependency>
......
</dependencies>
- groupId、artifactId、version:依赖的基本坐标。
- type:依赖的类型,对应项目对应的 packaging,一般不必声明。
- scope:依赖的作用范围。
- optional:标记依赖是否可选。
- exclusions:用来排除传递性依赖。
- classifier:区分从同一 artifact 构建的具有不同内容的构件。
1. scope 属性
- compile:默认范围,对编译、测试、运行三种 classpath 都有效。
- test:只对测试 classpath 有效,只需要在编译及运行测试时才需要,在打包时不会打进去。
- provided:对编译和测试 classpath 有效,但运行时无效。
- runtime:对测试和运行的 classpath 有效,但在编译主代码时无效。
system:与 provided 依赖范围一致,但使用该范围时必须通过 systemPath 元素显式地指定依赖文件的路径。由于此类依赖不是通过 Maven 仓库解析的,而且往往与本地仓库绑定,可能造成构建不可移植,因此要谨慎使用。systemPath 元素可以引用环境变量。
<dependencies>
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdxt</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
</dependencies>
import:只在 dependencyManagement 标签中生效,会导入已经定义好的 pom 文件中 dependencyManagement 节点内容。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.16.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2. optional 属性
A -> B、B -> X(可选)、B -> Y(可选)
项目 A 依赖于项目 B,项目 B 又依赖于项目 X 和 Y。理论上在项目 A 中,会把 B、X、Y 项目都依赖进来。但是 X、Y 两个依赖对于 B 来讲可能是互斥的,比如 B 是数据库隔离包,支持多种数据库 MySQL、Oracle,在构建 B 项目时,需要这两种数据库的支持,但在使用这个工具包时,只会依赖一个数据库。此时就需要在 B 项目中的 pom 文件里将 X、Y 依赖声明为可选的,具体如下:
<dependency>
<groupId>com.X</groupId>
<artifactId>X</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.Y</groupId>
<artifactId>Y</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
使用
Maven 生命周期
Maven 的生命周期是为了对所有构建过程进行抽象和统一,其中包含项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有的构建步骤。Maven 的生命周期是抽象的,本身不做任何实际的工作,实际的任务都交给插件来完成。这意味着 Maven 只在父类中定义了算法的整体结构,子类通过重写父类的方法来控制实际行为,类似设计模式中的模板方法。
Maven 的生命周期并不是一个整体,Maven 拥有三套相互独立的生命周期,具体如下:
- clean:生命周期的目的是清理项目。
- default:生命周期的目的是构建项目。
- site:生命周期的目的是建立项目站点。
每个生命周期包含一些阶段(phase),这些阶段之间是有依赖顺序的,以 clean 生命周期为例,它包含的阶段有 pre-clean、clean 和 post-clean。当调用 clean 的时候,pre-clean、clean、post-clean 阶段会相继顺序执行。三套生命周期本身是相互独立的,用户可以仅调用 clean 生命周期的某个阶段,或者仅调用 default 生命周期的某个阶段,而不会对其他生命周期产生任何影响。
1. clean
生命周期阶段 | 描述 |
---|---|
pre-clean | 执行一些清理前需要完成的工作 |
clean | 清理上一次构建生成的文件,主要是target目录 |
post-clean | 执行一些清理后需要完成的工作 |
2. default
共包含 23 个阶段,此处只介绍重点步骤,具体如下表:
生命周期阶段 | 描述 |
---|---|
validate | 检查工程配置是否正确,完成构建过程的所有必要信息是否能够获取到 |
initialize | 初始化构建状态 |
generate-sources | |
process-sources | 处理项目资源文件,处理项目的主资源文件。一般来说,是对 src/main/resources 目录的内容进行变量替换等工作后,复制到项目输出的主 classpath 目录中。 |
generate-resources | |
process-resources | |
compile | 编译项目的主源码。一般来说,是编译 src/main/java 目录下的 Java 文件至项目输出的主 classpath 目录中。 |
process-classes | 处理编译生成的文件,例如 Java Class 字节码的加密。 |
generate-test-sources | |
process-test-sources | 处理项目测试资源文件。一般来说,是对 src/test/resources 目录的内容进行变量替换等工作后,复制到项目输出的测试 classpath 目录中。 |
test-compile | 编译项目的测试代码。一般来说,是编译 src/test/java 目录下的 Java 文件至项目输出的测试 classpath 目录中。 |
process-test-classes | |
test | 使用适当的单元测试框架(例如JUnit)运行测试用例 |
prepare-package | 在真正打包之前,为准备打包执行任何必要的操作 |
package | 获取编译后的代码,并按照可发布的格式进行打包,例如 jar、war 等。 |
pre-integration-test | 在集成测试执行之前,执行所需的操作 |
integration-test | 处理和部署必须的工程包到集成测试能够运行的环境中 |
post-integration-test | 在集成测试被执行后执行必要的操作。例如,清理环境 |
verify | 运行检查操作来验证工程包是有效的,并满足质量要求。 |
install | 安装工程包到本地仓库中,该仓库可以作为本地其他工程的依赖。 |
deploy | 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程。 |
3. site
生命周期阶段 | 描述 |
---|---|
pre-site | 执行一些在生成项目站点之前需要完成的工作 |
site | 生成项目站点文档 |
post-site | 执行一些在生成项目站点之后需要完成的工作 |
site-deploy | 将生成的项目站点发布到服务器上 |
Maven 插件
Maven 的三套生命周期定义的各个阶段,实际上都是由 Maven 插件来完成的。一个插件一般有多个功能、每个功能就是一个目标(goal),而 Maven 生命周期的每个阶段就是由插件的目标来完成的。
如图,maven-compiler-plugin 是插件名称,compile 为插件功能,对应 default 生命周期的 compile 阶段。
为了实现快速构建,Maven 有一套内置的插件绑定。其中 default 生命周期的构建方式会其打包类型有关,打包类型在 POM 文件中的 packaging 指定。一般有 jar、war 两种类型。下图是三套生命周期的各个阶段与插件目标的绑定关系。
生命周期 | 阶段 | 插件名称及目标 |
---|---|---|
clean | pre-clean | |
clean | maven-clean-plugin:clean | |
post-clean | ||
site | pre-site | |
site | maven-site-plugin:site | |
post-site | ||
site-deploy | maven-site-plugin:deploy | |
default | process-resources | maven-resources-plugin:resources |
compile | maven-compiler-plugin:compile | |
process-test-resources | maven-resources-plugin:testResources | |
test-compile | maven-compiler-plugin:testCompile | |
test | maven-surefire-plugin:test | |
package | maven-jar-plugin:jar | |
install | maven-install-plugin:install | |
deploy | maven-deploy-plugin:deploy |
1. 自定义绑定
自定义绑定允许自己掌控插件目标与生命周期的结合。以生成项目主代码的源码 jar 为例。使用到的插件和它的目标为:maven-source-plugin:jar-no-fork。将其绑定到 default 生命周期阶段 verify 上(可以任意指定三套生命周期的任意阶段)。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<!-- 指定作用在生命周期的哪个阶段 -->
<phase>verify</phase>
<goals>
<!-- 指定执行绑定插件的哪些目标 -->
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2. 插件配置
**
在 maven 命令中加入 -D 参数,并伴随一个参数键=参数值的形式,来配置插件目标参数。例如:maven-surefire-plugin 插件提供一个 maven.test.skip 参数,当值为 true 时会跳过执行测试。
-- 对比 mvn install
mvn install –Dmaven.test.skip=true
使用 pom 全局配置:
在声明插件的时候,对插件进行一个全局配置,后面所有使用该插件的地方都要遵循这个配置。比如指定 maven-compile-plugin 编译 1.7 版本的源文件。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
Maven 常用操作
1. 聚合与继承
聚合:为了一次构建多个项目模块,就需要对多个项目模块进行聚合
<modules>
<module>模块一</module>
<module>模块二</module>
<module>模块三</module>
</modules>
继承:为了消除重复,把很多相同的配置提取出来,例如:dependency、grouptId,version 等
<parent>
<groupId>com.xxxx.maven</groupId>
<artifactId>parent-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../ParentProject/pom.xml</relativePath>
</parent>
两者共同点为:打方式必须都是 pom。在实际的项目中,一个 pom 既是聚合 pom 又是父 pom,但要注意:父 pom 中使用 dependencies 引入的依赖也会被子 pom 继承,所以不要将过多的实际依赖放在父 pom,父 pom 只用于管理,使用 dependencyManagement 标签。
2. 属性标签
通过
Maven 的内置属性:
- ${basedir} 表示项目根目录,即包含 pom.xml 文件的目录
- ${version} 表示项目版本
可以引用的 pom 属性:
所有 pom.xml 文件中的元素都可以用 project 前缀进行引用。常用的 POM 属性包括:
- ${project.build.sourceDirectory} : 项目的主源码目录,默认为 src/main/java/.
- ${project.build.testSourceDirectory} : 项目的测试源码目录,默认为 /src/test/java/.
- ${project.build.directory} : 项目构建输出目录,默认为 target/.
- ${project.build.outputDirectory} : 项目主代码编译输出目录,默认为 target/classes/.
- ${project.build.testOutputDirectory} : 项目测试代码编译输出目录,默认为 target/testclasses/.
- ${project.groupId}: 项目的 groupId.
- ${project.artifactId} : 项目的 artifactId.
- ${project.version} : 项目的 version, 等同于 ${version}
- ${project.build.finalName} : 项目打包输出文件的名称,默认为 ${project.artifactId}${project.version}
自定义属性:
<properties>
<swagger.version>2.2.2</swagger.version>
</properties>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
Settings 属性:
在 pom 文件中可以通过 settings 前缀来引用 settings.xml 中设定的属性,例如 ${settings.localRepository} 指向了用户本地仓库的地址。
Java 系统属性:
所有 Java 系统属性都可以使用 Maven 属性引用,例如 ${user.home} 指向了用户目录。可以通过命令行 mvn help:system 来查看所有的 Java 系统属性。
环境变量属性:
所有环境变量都可以用 env 前置的 Maven 属性进行引用。例如 ${env.JAVA_HOME} 指代了 JAVA_HOME 环境变量的值。通过命令行 mvn help:system 也可以查看所有的环境变量。
父级属性:
子工程想要引用父级工程的 pom.xml 文件中的属性变量可以用 parent 前缀进行引用。例如 ${parent.version} 表示引用父级工程里的 version 属性。
3. Profile
在开发过程中,我们的软件会面对不同的运行环境,比如开发环境、测试环境、生产环境,而在不同的环境中部分配置可能会不一样,比如数据源配置、日志文件配置、以及一些软件运行过程中的基本配置,如果每次我们将软件部署到不同的环境时,都需要修改相应的配置文件的话,即容易出错,又浪费时间。
为此,Maven 提供了一种方便的解决方案,就是 profile 功能。在一个 pom.xml 文件中可以定义多个 profile,每个 profile 会对应不同的激活条件和配置信息,从而达到不同环境使用不同配置信息的效果。profile 可以在以下几个地方声明:
- pom.xml:这里声明的 profile 只对当前项目有效
- 用户 settings.xml:.m2/settings.xml 中的 profile 对该用户的 Maven 项目有效
- 全局 settings.xml:conf/settings.xml,对本机上所有 Maven 项目有效
如下示例:
<profiles>
<profile>
<!-- 本地开发环境 -->
<id>dev</id>
<properties>
<profiles.active>dev</profiles.active>
</properties>
<!-- 在该 profile 下才会引入的依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
<dependencies>
<activation>
<!-- 设置默认激活这个配置 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<!-- 发布环境 -->
<id>release</id>
<properties>
<profiles.active>release</profiles.active>
</properties>
</profile>
<profile>
<!-- 测试环境 -->
<id>beta</id>
<properties>
<profiles.active>beta</profiles.active>
</properties>
</profile>
</profiles>
4. 多仓库配置
当只配置一个 Maven 仓库时,操作比较简单,直接在 Maven 的 settings.xml 文件中的
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
镜像配置说明:
- id:镜像的唯一标识
- name:名称描述
- url:仓库地址
- mirrorOf:指定镜像规则,即什么情况下从该镜像仓库中拉取,其中 * 表示匹配所有。
而针对 Maven 的多仓库配置如果在
<profiles>
<profile>
<id>dev</id>
<repositories>
<repository>
<id>dev</id>
<url>https://repo.dev.com/main/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
</profile>
<profile>
<id>aliyun</id>
<repositories>
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
</profile>
<profiles>
当配置完成后,需要通过配置 <activeProfiles> 子节点来激活配置:
<activeProfiles>
<activeProfile>dev</activeProfile>
<activeProfile>aliyun</activeProfile>
</activeProfiles>
此时如果在 Idea 中配置了 Maven,那么在项目的 Maven 管理中会看到下图中的 profile 选项,当通过 Idea 执行 Maven 命令时,直接勾选对应的 profile 即可。
如果是在命令行中使用 Maven 命令的话,通过 -P 参数指定需要使用的 profile 即可,如下所示:
mvn -P aliyun ...
5. 使用私服仓库
首先修改 setting.xml 文件,在 <servers> 中指定私服仓库的用户名和密码。
<servers>
<server>
<id>customeRepo</id>
<username>admin</username>
<password>123</password>
</server>
</servers>
然后在项目的 pom.xml 文件中加入 <distributionManagement> 节点,在该节点中指定私服仓库的 id 及仓库地址即可。注意:
<distributionManagement>
<repository>
<id>customeRepo</id>
<url>http://xxx/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>customeRepo</id>
<url>http://xxx/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
Maven 常用命令
- mvn clean:清理项目生产的临时文件,一般是模块下的 target 目录
- mvn package:项目打包工具,会在模块下的 target 目录生成 jar 或 war 等文件
- mvn test:测试命令,会执行 src/test/java 下 Junit 的测试用例,可通过 -Dmaven.test.skip=true 跳过测试流程
- mvn install:模块安装命令,将打包好的 jar 或 war 文件复制到本地 Maven 仓库中,供其他模块使用
- mvn deploy:发布命令,将打包的文件发布到远程仓库
- mvn dependency:analyze:分析项目依赖关系, 用来分析出无用及重复的依赖
- mvn dependency:tree:打印出项目的整个依赖树
- mvn dependency:resolve:查看依赖
- mvn project-info-reports:dependencies:生成项目依赖的报表
如果想要安装本地 jar 文件,可执行如下命令:
mvn install:install-file
-Dfile=you.jar
-DgroupId=org.csource.fastdfs
-DartifactId=fastdfs
-Dversion=1.25
-Dpackaging=jar
- -Dfile:本地 jar 包位置,即未安装至本地仓库前的 jar 包文件的位置路径
- -DgroupId:项目名 对应
com.jacob - -DartifactId:文件名 对应
jacob - -Dversion:版本号 对应
1.8
此外,执行 Maven 命令还可以附加参数选项,具体如下所示:其他参数可以通过 mvn -h 获取:
- -D:传入属性参数,例如 mvn install -Dmaven.test.skip=true
- -P:指定要激活使用的 profile 配置 ,例如 mvn install -P aliyun
- -e:显示 Maven 运行出错的信息
- -o:离线模式,即执行命令时不会去远程仓库更新依赖
- -U:强制去远程仓库更新 snapshot 版本的依赖