Maven 是 Apache 开源的跨平台项目管理工具,主要服务基于 Java 平台的项目构建、依赖管理和项目信息管理等,类似于 Linux 的 yum。其 POM(Project Object Model)文件是 Maven 最核心的配置文件。

Maven 项目骨架:

  1. 根目录:工程名
  2. |---src:源码
  3. |---|---main:主程序
  4. |---|---|---java:主程序代码路径
  5. |---|---|---resource:主程序配置文件路径
  6. |---|---test:测试
  7. |---|---|---java:测试代码路径
  8. |---|---|---resource:测试配置文件路径
  9. |---pom.xmlmaven 配置文件

Maven 坐标与依赖

Maven 坐标可以类比数学中平面几何的坐标,任何一个坐标都能唯一标识平面中的一个点。这个点在 Maven 中就是对应的 .jar.war 等文件。Maven 针对每一个坐标文件提供了多个属性配置来构建自己的坐标,只要能提供正确配置的坐标元素,Maven 就能找到对应的坐标文件。基本坐标元素如下:

  • groupId:定义当前 Maven 项目隶属的实际项目。
  • artifactId:定义实际项目中的一个 Maven 项目(模块)。
  • version:定义 Maven 项目当前所处的版本。
  • packaging:定义 Maven 项目打包方式。jar、war、pom。默认为 jar。
  1. <dependencies>
  2. <dependency>
  3. <groupId></groupId>
  4. <artifactId></artifactId>
  5. <version></version>
  6. <classifier></classifier>
  7. <exclusions>
  8. <exclusion>
  9. <groupId></groupId>
  10. <artifactId></artifactId>
  11. </exclusion>
  12. ......
  13. </exclusions>
  14. <optional></optional>
  15. <scope></scope>
  16. <type></type>
  17. </dependency>
  18. ......
  19. </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 元素可以引用环境变量。

    1. <dependencies>
    2. <dependency>
    3. <groupId>javax.sql</groupId>
    4. <artifactId>jdbc-stdxt</artifactId>
    5. <version>2.0</version>
    6. <scope>system</scope>
    7. <systemPath>${java.home}/lib/rt.jar</systemPath>
    8. </dependency>
    9. </dependencies>
  • import:只在 dependencyManagement 标签中生效,会导入已经定义好的 pom 文件中 dependencyManagement 节点内容。

    1. <dependencyManagement>
    2. <dependencies>
    3. <dependency>
    4. <groupId>org.springframework</groupId>
    5. <artifactId>spring-framework-bom</artifactId>
    6. <version>4.3.16.RELEASE</version>
    7. <type>pom</type>
    8. <scope>import</scope>
    9. </dependency>
    10. </dependencies>
    11. </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 依赖声明为可选的,具体如下:

  1. <dependency>
  2. <groupId>com.X</groupId>
  3. <artifactId>X</artifactId>
  4. <version>1.0</version>
  5. <optional>true</optional>
  6. </dependency>
  7. <dependency>
  8. <groupId>com.Y</groupId>
  9. <artifactId>Y</artifactId>
  10. <version>1.0</version>
  11. <optional>true</optional>
  12. </dependency>

使用 元素标识以后,只会对当前项目 B 产生影响,当其他的项目依赖 B 项目时,这两个依赖都不会被传递。

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 生命周期的每个阶段就是由插件的目标来完成的。
image.png
如图,maven-compiler-plugin 是插件名称,compile 为插件功能,对应 default 生命周期的 compile 阶段。
image.png
为了实现快速构建,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 上(可以任意指定三套生命周期的任意阶段)。

  1. <build>
  2. <plugins>
  3. <plugin>
  4. <groupId>org.apache.maven.plugins</groupId>
  5. <artifactId>maven-source-plugin</artifactId>
  6. <version>2.1.1</version>
  7. <executions>
  8. <execution>
  9. <id>attach-sources</id>
  10. <!-- 指定作用在生命周期的哪个阶段 -->
  11. <phase>verify</phase>
  12. <goals>
  13. <!-- 指定执行绑定插件的哪些目标 -->
  14. <goal>jar-no-fork</goal>
  15. </goals>
  16. </execution>
  17. </executions>
  18. </plugin>
  19. </plugins>
  20. </build>

2. 插件配置

**
在 maven 命令中加入 -D 参数,并伴随一个参数键=参数值的形式,来配置插件目标参数。例如:maven-surefire-plugin 插件提供一个 maven.test.skip 参数,当值为 true 时会跳过执行测试。

  1. -- 对比 mvn install
  2. mvn install Dmaven.test.skip=true

使用 pom 全局配置:
在声明插件的时候,对插件进行一个全局配置,后面所有使用该插件的地方都要遵循这个配置。比如指定 maven-compile-plugin 编译 1.7 版本的源文件。

  1. <plugin>
  2. <groupId>org.apache.maven.plugins</groupId>
  3. <artifactId>maven-compiler-plugin</artifactId>
  4. <configuration>
  5. <fork>true</fork>
  6. <source>1.7</source>
  7. <target>1.7</target>
  8. </configuration>
  9. </plugin>

Maven 常用操作

1. 聚合与继承

聚合:为了一次构建多个项目模块,就需要对多个项目模块进行聚合

  1. <modules>
  2. <module>模块一</module>
  3. <module>模块二</module>
  4. <module>模块三</module>
  5. </modules>

继承:为了消除重复,把很多相同的配置提取出来,例如:dependency、grouptId,version 等

  1. <parent>
  2. <groupId>com.xxxx.maven</groupId>
  3. <artifactId>parent-project</artifactId>
  4. <version>0.0.1-SNAPSHOT</version>
  5. <relativePath>../ParentProject/pom.xml</relativePath>
  6. </parent>

两者共同点为:打方式必须都是 pom。在实际的项目中,一个 pom 既是聚合 pom 又是父 pom,但要注意:父 pom 中使用 dependencies 引入的依赖也会被子 pom 继承,所以不要将过多的实际依赖放在父 pom,父 pom 只用于管理,使用 dependencyManagement 标签。

2. 属性标签

通过 元素,用户可以自定义一个或多个 Maven 属性,然后在 POM 文件的其他的地方使用 ${属性名} 的方式引用该属性,这种方式的最大意义在于消除重复属性。

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}

自定义属性:

  1. <properties>
  2. <swagger.version>2.2.2</swagger.version>
  3. </properties>
  4. <dependency>
  5. <groupId>io.springfox</groupId>
  6. <artifactId>springfox-swagger2</artifactId>
  7. <version>${swagger.version}</version>
  8. </dependency>

Settings 属性:
在 pom 文件中可以通过 settings 前缀来引用 settings.xml 中设定的属性,例如 ${settings.localRepository} 指向了用户本地仓库的地址。

Java 系统属性:
所有 Java 系统属性都可以使用 Maven 属性引用,例如 ${user.home} 指向了用户目录。可以通过命令行 mvn help:system 来查看所有的 Java 系统属性。
image.png

环境变量属性:
所有环境变量都可以用 env 前置的 Maven 属性进行引用。例如 ${env.JAVA_HOME} 指代了 JAVA_HOME 环境变量的值。通过命令行 mvn help:system 也可以查看所有的环境变量。
image.png

父级属性:
子工程想要引用父级工程的 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 项目有效

如下示例:

  1. <profiles>
  2. <profile>
  3. <!-- 本地开发环境 -->
  4. <id>dev</id>
  5. <properties>
  6. <profiles.active>dev</profiles.active>
  7. </properties>
  8. <!-- 在该 profile 下才会引入的依赖 -->
  9. <dependencies>
  10. <dependency>
  11. <groupId>org.springframework</groupId>
  12. <artifactId>spring-context</artifactId>
  13. <version>3.2.4.RELEASE</version>
  14. </dependency>
  15. <dependencies>
  16. <activation>
  17. <!-- 设置默认激活这个配置 -->
  18. <activeByDefault>true</activeByDefault>
  19. </activation>
  20. </profile>
  21. <profile>
  22. <!-- 发布环境 -->
  23. <id>release</id>
  24. <properties>
  25. <profiles.active>release</profiles.active>
  26. </properties>
  27. </profile>
  28. <profile>
  29. <!-- 测试环境 -->
  30. <id>beta</id>
  31. <properties>
  32. <profiles.active>beta</profiles.active>
  33. </properties>
  34. </profile>
  35. </profiles>

4. 多仓库配置

当只配置一个 Maven 仓库时,操作比较简单,直接在 Maven 的 settings.xml 文件中的 标签中进行全局配置即可,以阿里云的镜像为例:

  1. <mirrors>
  2. <mirror>
  3. <id>alimaven</id>
  4. <name>aliyun maven</name>
  5. <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  6. <mirrorOf>central</mirrorOf>
  7. </mirror>
  8. </mirrors>

镜像配置说明:

  • id:镜像的唯一标识
  • name:名称描述
  • url:仓库地址
  • mirrorOf:指定镜像规则,即什么情况下从该镜像仓库中拉取,其中 * 表示匹配所有。

而针对 Maven 的多仓库配置如果在 标签中多配置几个 mirror 是不会生效的。正确的操作是要在 <profiles> 节点下配置多个 <profile>,这里的 id 就是 中指定的仓库 id。

  1. <profiles>
  2. <profile>
  3. <id>dev</id>
  4. <repositories>
  5. <repository>
  6. <id>dev</id>
  7. <url>https://repo.dev.com/main/</url>
  8. <releases>
  9. <enabled>true</enabled>
  10. </releases>
  11. <snapshots>
  12. <enabled>true</enabled>
  13. <updatePolicy>always</updatePolicy>
  14. </snapshots>
  15. </repository>
  16. </repositories>
  17. </profile>
  18. <profile>
  19. <id>aliyun</id>
  20. <repositories>
  21. <repository>
  22. <id>aliyun</id>
  23. <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  24. <releases>
  25. <enabled>true</enabled>
  26. </releases>
  27. <snapshots>
  28. <enabled>true</enabled>
  29. <updatePolicy>always</updatePolicy>
  30. </snapshots>
  31. </repository>
  32. </repositories>
  33. </profile>
  34. <profiles>

当配置完成后,需要通过配置 <activeProfiles> 子节点来激活配置:

  1. <activeProfiles>
  2. <activeProfile>dev</activeProfile>
  3. <activeProfile>aliyun</activeProfile>
  4. </activeProfiles>

此时如果在 Idea 中配置了 Maven,那么在项目的 Maven 管理中会看到下图中的 profile 选项,当通过 Idea 执行 Maven 命令时,直接勾选对应的 profile 即可。
image.png
如果是在命令行中使用 Maven 命令的话,通过 -P 参数指定需要使用的 profile 即可,如下所示:

  1. mvn -P aliyun ...

5. 使用私服仓库

首先修改 setting.xml 文件,在 <servers> 中指定私服仓库的用户名和密码。

  1. <servers>
  2. <server>
  3. <id>customeRepo</id>
  4. <username>admin</username>
  5. <password>123</password>
  6. </server>
  7. </servers>

然后在项目的 pom.xml 文件中加入 <distributionManagement> 节点,在该节点中指定私服仓库的 id 及仓库地址即可。注意: 里的 id 需要和上一步里 里配置的 id 名称保持一致。

  1. <distributionManagement>
  2. <repository>
  3. <id>customeRepo</id>
  4. <url>http://xxx/repository/maven-releases/</url>
  5. </repository>
  6. <snapshotRepository>
  7. <id>customeRepo</id>
  8. <url>http://xxx/repository/maven-snapshots/</url>
  9. </snapshotRepository>
  10. </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 文件,可执行如下命令:

  1. mvn install:install-file
  2. -Dfile=you.jar
  3. -DgroupId=org.csource.fastdfs
  4. -DartifactId=fastdfs
  5. -Dversion=1.25
  6. -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 版本的依赖

image.png