Maven是一个Java项目管理和构建工具,它可以定义项目结构、项目依赖,并使用统一的方式进行自动化构建,是Java项目不可缺少的工具。
Maven就是是专门为Java项目打造的管理和构建工具,它的主要功能有:

  • 提供了一套标准化的项目结构;
  • 提供了一套标准化的构建流程(编译,测试,打包,发布……);
  • 提供了一套依赖管理机制。

    1.Maven项目结构

    一个使用Maven管理的普通的Java项目,它的目录结构默认如下:
    image.png
    项目的根目录a-maven-project是项目名,它有一个项目描述文件pom.xml,存放Java源码的目录是src/main/java,存放资源文件的目录是src/main/resources,存放测试源码的目录是src/test/java,存放测试资源的目录是src/test/resources,最后,所有编译、打包生成的文件都放在target目录里。这些就是一个Maven项目的标准目录结构。
    所有的目录结构都是约定好的标准结构,我们千万不要随意修改目录结构。使用标准结构不需要做任何配置,Maven就可以正常使用。
    我们再来看最关键的一个项目描述文件pom.xml,它的内容长得像下面:

    1. <project ...>
    2. <modelVersion>4.0.0</modelVersion>
    3. <groupId>com.itranswarp.learnjava</groupId>
    4. <artifactId>hello</artifactId>
    5. <version>1.0</version>
    6. <packaging>jar</packaging>
    7. <properties>
    8. ...
    9. </properties>
    10. <dependencies>
    11. <dependency>
    12. <groupId>commons-logging</groupId>
    13. <artifactId>commons-logging</artifactId>
    14. <version>1.2</version>
    15. </dependency>
    16. </dependencies>
    17. </project>

    其中,groupId类似于Java的包名,通常是公司或组织名称,artifactId类似于Java的类名,通常是项目名称,再加上version,一个Maven工程就是由groupId,artifactId和version作为唯一标识。我们在引用其他第三方库的时候,也是通过这3个变量确定。例如,依赖commons-logging:

    1. <dependency>
    2. <groupId>commons-logging</groupId>
    3. <artifactId>commons-logging</artifactId>
    4. <version>1.2</version>
    5. </dependency>

    使用声明一个依赖后,Maven就会自动下载这个依赖包并把它放到classpath中。
    Maven是一个Java项目的管理和构建工具:

  • Maven使用pom.xml定义项目内容,并使用预设的目录结构;

  • 在Maven中声明一个依赖项可以自动下载并导入classpath;
  • Maven使用groupId,artifactId和version唯一定位一个依赖。

    2.依赖管理

    Maven解决了依赖管理问题。例如,我们的项目依赖abc这个jar包,而abc又依赖xyz这个jar包。

    1.依赖关系

    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. <dependency>
  2. <groupId>org.junit.jupiter</groupId>
  3. <artifactId>junit-jupiter-api</artifactId>
  4. <version>5.3.2</version>
  5. <scope>test</scope>
  6. </dependency>

runtime依赖表示编译时不需要,但运行时需要。最典型的runtime依赖是JDBC驱动,例如MySQL驱动:

  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <version>5.1.48</version>
  5. <scope>runtime</scope>
  6. </dependency>

provided依赖表示编译时需要,但运行时不需要。最典型的provided依赖是Servlet API,编译的时候需要,但是运行时,Servlet服务器内置了相关的jar,所以运行期不需要:

  1. <dependency>
  2. <groupId>javax.servlet</groupId>
  3. <artifactId>javax.servlet-api</artifactId>
  4. <version>4.0.0</version>
  5. <scope>provided</scope>
  6. </dependency>

2.唯一ID

对于某个依赖,Maven只需要3个变量即可唯一确定某个jar包:

  • groupId:属于组织的名称,类似Java的包名;
  • artifactId:该jar包自身的名称,类似Java的类名;
  • version:该jar包的版本。

通过上述3个变量,即可唯一确定某个jar包。Maven通过对jar包进行PGP签名确保任何一个jar包一经发布就无法修改。修改已发布jar包的唯一方法是发布一个新版本。
因此,某个jar包一旦被Maven下载过,即可永久地安全缓存在本地。
注:只有以-SNAPSHOT结尾的版本号会被Maven视为开发版本,开发版本每次都会重复下载,这种SNAPSHOT版本只能用于内部私有的Maven repo,公开发布的版本不允许出现SNAPSHOT。

3.Maven镜像

除了可以从Maven的中央仓库下载外,还可以从Maven的镜像仓库下载。如果访问Maven的中央仓库非常慢,我们可以选择一个速度较快的Maven的镜像仓库。Maven镜像仓库定期从中央仓库同步。
中国区用户可以使用阿里云提供的Maven镜像仓库。使用Maven镜像仓库需要一个配置,在用户主目录下进入.m2目录,创建一个settings.xml配置文件,内容如下:

  1. <settings>
  2. <mirrors>
  3. <mirror>
  4. <id>aliyun</id>
  5. <name>aliyun</name>
  6. <mirrorOf>central</mirrorOf>
  7. <!-- 国内推荐阿里云的Maven镜像 -->
  8. <url>https://maven.aliyun.com/repository/central</url>
  9. </mirror>
  10. </mirrors>
  11. </settings>

4.搜索第三方组件

如果我们要引用一个第三方组件,比如okhttp,如何确切地获得它的groupId、artifactId和version?方法是通过search.maven.org搜索关键字,找到对应的组件后,直接复制。

5.命令行编译

在命令中,进入到pom.xml所在目录,输入以下命令:

  1. $ mvn clean package

如果一切顺利,即可在target目录下获得编译后自动打包的jar。
Maven通过解析依赖关系确定项目所需的jar包,常用的4种scope有:compile(默认),test,runtime和provided;
Maven从中央仓库下载所需的jar包并缓存在本地;
可以通过镜像仓库加速下载。

3.构建流程

Maven不但有标准化的项目结构,而且还有一套标准化的构建流程,可以自动化实现编译,打包,发布,等等。

1.Lifecycle和Phase

使用Maven时,我们首先要了解什么是Maven的生命周期(lifecycle)。
Maven的生命周期由一系列阶段(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 clean:清理所有生成的class和jar;
mvn clean compile:先清理,再执行到compile;
mvn clean test:先清理,再执行到test,因为执行test前必须执行compile,所以这里不必指定compile;
mvn clean package:先清理,再执行到package。
大多数phase在执行过程中,因为我们通常没有在pom.xml中配置相关的设置,所以这些phase什么事情都不做。
经常用到的phase其实只有几个:

  • clean:清理
  • compile:编译
  • test:运行测试
  • package:打包

    2.Goal

    执行一个phase又会触发一个或多个goal:
执行的Phase 对应执行的Goal
compile compiler:compile
test compiler:testCompile
surefire:test

goal的命名总是abc:xyz这种形式。
类比一下就明白了:

  • lifecycle相当于Java的package,它包含一个或多个phase;
  • phase相当于Java的class,它包含一个或多个goal;
  • goal相当于class的method,它其实才是真正干活的。

大多数情况,我们只要指定phase,就默认执行这些phase默认绑定的goal,只有少数情况,我们可以直接指定运行一个goal,例如,启动Tomcat服务器:

  1. mvn tomcat:run

Maven通过lifecycle、phase和goal来提供标准的构建流程。
最常用的构建命令是指定phase,然后让Maven执行到指定的phase:

  • mvn clean
  • mvn clean compile
  • mvn clean test
  • mvn clean package

通常情况,我们总是执行phase默认绑定的goal,因此不必指定goal。

4.插件

Maven自带的标准插件例如compiler是无需声明的,只有引入其它的插件才需要声明。
下面列举了一些常用的插件:

  • maven-shade-plugin:打包所有依赖包并生成可执行jar;
  • cobertura-maven-plugin:生成单元测试覆盖率报告;
  • findbugs-maven-plugin:对Java源码进行静态分析以找出潜在问题。

Maven通过自定义插件可以执行项目构建时需要的额外功能,使用自定义插件必须在pom.xml中声明插件及配置;
插件会在某个phase被执行时执行;
插件的配置和用法需参考插件的官方文档。

5.模块化管理

Maven支持模块化管理,可以把一个大项目拆成几个模块:

  • 可以通过继承在parent的pom.xml统一定义重复配置;
  • 可以通过编译多个模块。

    6.mvnw

    mvnw是Maven Wrapper的缩写。
    简单地说,Maven Wrapper就是给一个项目提供一个独立的,指定版本的Maven给它使用。
    Maven Wrapper的另一个作用是把项目的mvnw、mvnw.cmd和.mvn提交到版本库中,可以使所有开发人员使用统一的Maven版本。

    7.发布Artifact

    使用Maven发布一个Artifact时:

  • 可以发布到本地,然后推送到远程Git库,由静态服务器提供基于网页的repo服务,使用方必须声明repo地址;

  • 可以发布到central.sonatype.org,并自动同步到Maven中央仓库,需要前期申请账号以及本地配置;
  • 可以发布到GitHub Packages作为私有仓库使用,必须提供Token以及正确的权限才能发布和使用。