乐趣区

maven通用教程

看到一份 2018 年 JVM 生态报告提到,使用构建工具的比例,maven 高达 60%,远高于 gradle 的 19%。

我平常也使用 maven,于是就整理了一些 maven 的常用知识。
关于 maven 的博文已经浩如烟海,所以这里我只是总结一些自己常用知识,以做备忘。

仓库

分为本地仓库和远程仓库
远程仓库默认使用的是 Maven 社区提供的中央仓库

还有其他社区提供的仓库,如 jcenter,jboss。
一般公司都会使用 Nexus 部署一个私服(也是仓库),用于存放公司内部构件。
mvn deploy 可以将项目生成的构件分发到私服。

镜像

因为中央仓库一般部署在外国,下载构件速度比较慢,所以可以通过镜像 mirror 下载,mirrorOf 配置代理仓库。
当需要从代理仓库下载构件时,都会转为从镜像下载。
镜像中不存在该构件时,镜像会从仓库下载,缓存到镜像中。
需要注意的是,由于镜像完全屏蔽了代理仓库,当镜像不稳定或者停止服务的时候,Maven 不会直接访问代理仓库,因而将无法下载构件。
最常用的是阿里提供的镜像服务:

<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>central</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

profile

profile 用于定义不同环境的不同配置,如开发,测试环境使用不同的仓库,打包方式等。
mvn clean package -Ppro 即构建出 pro 环境需要的 war 包

构造

构造 Java 项目
mvn archetype:generate -DinteractiveMode=false -DgroupId=com.binecy -DartifactId=my-core

构造 Web 项目
mvn archetype:generate -DinteractiveMode=false -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.binecy -DartifactId=my-web

构造子模块
构造父模块
# mvn archetype:generate -DinteractiveMode=false -DgroupId=com.binecy -DartifactId=system-core
进入 system-core 项目
# cd system-core
删除不需要的 src 文件夹
# rm src

修改 pom.xml 文件,将 <packaging>jar</packaging> 修改为 <packaging>pom</packaging>
构造子模块 system-dao
# mvn archetype:generate -DinteractiveMode=false -DgroupId=com.binecy -DartifactId=system-dao
构造子模块 system-web
# mvn archetype:generate -DinteractiveMode=false -DgroupId=com.binecy -DartifactId=system-web

这时父模块的 pom.xml 已经自动添加了 module 信息

<?xml version="1.0" encoding="UTF-8"?>
<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.binecy</groupId>
  <artifactId>system-core</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>system-core</name>
  <url>http://maven.apache.org</url>

  <modules>
    <module>system-service</module>
    <module>system-web</module>
  </modules>
</project>

一个多模块的项目构造完成。

dependencyManagement
父模块中定义的 dependencies,子模块默认继承,不需重复定义也可以使用。
父模块中定义的 dependencyManagement 只是声明依赖,并不实现引入,如果子模块也定义了该依赖,可以继承父模块 dependencyManagement 中定义的 version 和 scope,不用需要声明这两个属性。
如果子模块也声明了 version 属性,则使用子模块声明的版本。

跳过测试
mvn install -Dmaven.test.skip=true

使用 tomcat 插件

      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <path>/</path>
          <port>9090</port>
          <uriEncoding>UTF-8</uriEncoding>
        </configuration>
      </plugin>

依赖冲突

依赖传递
如果我们的项目 A 依赖构件 B,而构件 B 又依赖于构件 C,依赖的关系为:A—>B—>C
当我们执行项目 A,maven 会下载构件 B,构件 C

依赖冲突
如果有一下依赖关系
A—>B—>C(version:0.0.1)
A—>D>—C(version:0.0.2)
那么项目 A 引入哪个版本的构件 C 呢?

这里有两个重要的依赖调解原则。

1:如果依赖路径的长度不同,则“短路优先”:

     A—>B—>C—>D—>E—>X(version 0.0.1)
     A—>F—>X(version 0.0.2)
     则 A 依赖于 X(version 0.0.2)。

2:依赖路径长度相同情况下,则“先声明优先”:

     A—>E—>X(version 0.0.1)
     A—>F—>X(version 0.0.2)
     如果项目 A 的 depencies 先声明引入 E,这 A 依赖于 X(version 0.0.1)

参考:Maven 依赖传递、依赖传递排除、依赖冲突

依赖冲突,通常是低版本的依赖覆盖了高版本的依赖,而某些构件使用了高版本特有的功能导致的。这时可以使用 dependency:tree 分析依赖关系,使用 exclusions 排除重复的依赖或者添加优化级更高的正确依赖。

参考:
Maven 核心原理

退出移动版