Maven3 Core Overview

Maven是一个项目管理工具,它包含了一个项目对象模型(Project Object Model,POM) ,一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(Phase)中插件(Plugin)目标(Goal)的逻辑。

下图描述了Maven是如何使用POM的,以及POM文件中包含了什么

构建生命周期

生命周期是Maven为了对所有项目的构件过程进行统一和抽象。基于所有的项目构建,都能映射到这样一个生命周期上。Maven生命周期本身是抽象的,那么所有的工作都是交给插件完成的。

不仅如此,Maven总共内建了三套生命周期,这三套都是相互独立的:

  • default,这是默认的生命周期,用于部署项目

  • clean,用于清理项目,比如编译的文件等,一般都存在于target目录中

  • site,用于为当前项目创建web站点

每个生命周期又包含了一些阶段(Phase)default的生命周期由以下几个阶段(Phase)组成:

validate : 校验项目是正确的,并且所有的信息可用

compile :编译

test : 进行单元测试

package : 获取已经编译的源码,并进行打包

verify :检查测试结果

install :将软件包安装到本地,用作本地其他项目的依赖项

deploy :将当前项目发布到远程仓库,提供给其他人用

default 被使用,就会按照上面的生命周期从上到下构建

更加详细的可以查看官方文档

阶段的执行顺序

生命周期的阶段是按顺序执行的,例如

当想进行单元测试,可以通过使用test 进行,执行如下命令:

mvn test

当执行完该执行,会依次执行validatecompiletest

如果不确定需要什么,首选的调用是

mvn verify

在构建环境中,可以使用下面的指令感觉的构建发布到共享存储库中

mvn clean deploy

真正做工作的还是绑定在阶段(Phase)上的插件(Plugin)目标(goal),一个阶段可以绑定零个或多个目标

插件和插件执行目标

基于 约定优先于配置(Convention Over Configuration),Maven为项目提供了定义好的生命周期和一组通用插件。这样的好处就是减少配置,不容易因为配置错误出问题。

Maven是一个以Plugin为核心的框架(或者说将主要的工作委派给了插件)。所有的工作由插件完成,一个插件有一个或多个目标,每个目标代表着插件的不同能力,这样能够最大的提升代码的复用能力。

例如,编译插件有两个目标compiletestCompile,前者源码编译,后者编译测试代码,这对应着default 生命周期的两个阶段,compiletest-compile。所以具体来说,就是插件的目标对应着生命周期的阶段

插件有两个类别:

  • Build plugins构建插件,就是

  • Reporting Plugins 是生成站点的一部分,通过在POM中的 <reporting/> 进行配置

这里可以找到一些常见的插件

目标具有一些参数,这个得查阅具体的文档。例如 compiler:compiler ,这里有它的一些参数

可以通过在pom声明并使用一个插件

<project>
[...]
<build>
 <finalName>simple-webapp</finalName>
 <plugins>
   <plugin>
       <!-- 指定插件的groupId 和 artifactId -->
       <groupId></groupId>
       <artifactId></artifactId>
   </plugin>
 </plugins>
</build>
[...]
</project>

Maven中的三类生命周期的每个阶段都绑定了不同的插件的不同目标

Default生命周期绑定了这些插件。当然,这些也可以自己指定:

<plugins>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<executions>
  <execution>
    <id></id>
    <phase>compile</phase>
    <goalos>
      <goal>pre-compile</goal>
    </goals>
  </execution>
</executions>
</plugins>

之后,可以在命令行来调用这些插件,格式如下

mvn [options] [<goal(s)>] [<phase()s>]

有两种写法来调用

完整格式

mvn mvn-dependency-plugin:tree

简化版

mvn dependency:tree

简化版是通过maven插件仓库定义的元数据来建立映射关系的:

<plugin>
 <name>Apache Maven Dependency Plugin</name>
 <prefix>dependency</prefix>
 <artifactId>maven-dependency-plugin</artifactId>
</plugin>

当然,指定仓库元数据也可以。 在setting.xml 中指定:

<settings>
<pluginGroups>
<pluginGroup>com.your.plugins</pluginGroup>
</pluginGroups>
</settings>

指定后,Maven就会检查该仓库的元数据。

基本的目录结构

下面是一个项目的目录结构

myprojectdir
- pom.xml - .mvn
- jvm.config - src
- main
- java
- resources
- test
- java
- resources - target

更加详细的目录布局规范

POM

POM(Project Object Model ,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖。

所有的POM都继承自一个super POM(如果当前POM没有指定parent)

其中定义了基本了目录结构、一个默认的插件仓库和包仓库

以下是最简的POM

<project>
<modelVersion>4.0.0</modelVersion>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
</project>

用坐标(Coordinates)标识一个项目

Maven坐标定义了一组标识,它们以用来标识一个项目,一个依赖,或者Maven POM里的一个插件,下面是一个示例

<project>
<groupId>org.example</groupId>
<artifactId>maven-practices</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<modules>
<dependencies>
.....
</dependencies>
<build>
....
</build>
</project>

Maven坐标由三个标签组成:

  • groupId

    创建这个项目的小组/组织/公司名称的逆向域名开头

  • artifactId

    在groupId下的表示一个单独项目的唯一标识符*

  • version

    项目的特定版本。发布的项目有一个固定的版本,而在开发的项目可以用一个特殊的标识,这种标识给版本后加上一个SNAPSHOT(快照)标识*

项目的打包方式:

  • packaging

    项目的类型,默认是jar,描述了项目打包后的输出。类型为jar产生JAR文件,类型为war产生一个Web应用

依赖管理

每个项目工程可以依赖一些开源的构建来提高项目的开发效率,或者是一些开源的插件来执行编译、测试、打包等工作,最后将项目生成的构建安装到本地仓库中。这模块就能供其他Maven项目使用了。

依赖配置

<project>
<dependencies>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<type></type>
<scope></scope>
<optional></optional>
<exclusions>
<exclusion>
<groupId></groupId>
<artifactId></artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

跟元素Project下的dependencies可以包含一个或多个dependency元素,以声明一个或多个项目以来。每个依赖可以包含一下几个元素:

  • groupId、artifactId和version :基本的坐标

  • type:依赖的类型,对应于项目坐标定义的packaging。默认值为jar,改类型还有一下几个值,具体的介绍可以查看官方文档

    • war,通常对应的Web项目

    • ejb-client

    • test-jar

  • scope:依赖的范围。maven在编译(compile )classpath、测试(test)classpath、运行(runtime)classpath三个不同的阶段使用三个不同的classpath ,依赖范围就是用来控制这三种classpath的关系。Maven有以下几种依赖范围:

    • compile:编译依赖范围。默认的依赖范围。使用该依赖范围的Maven依赖,对于以上三种classpath都有效。

    • test:测试依赖范围。只在test 范围有效。例如Junit,它只在需要测试代码时才使用到。

    • provided:已提供依赖范围。在compile和test有效。例如servlet-api,编译和测试的时候许哟使用该依赖,但在运行时无效。典型的例子是servlet-api,由于容器已经提供,就不许哟Maven重复地引入一边

    • runtime:运行时依赖范围。只在runtime和test时有效。例如JDBC驱动实现,项目主代码的编译只需哟JDK提供的JDBC借口,只有在执行测试或者运行项目的时候才需要实现上述借口的具体JDBC驱动。

    • system:系统依赖范围。使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径。该路径与本地系统绑定,移植性低。例如Linux和windows系统中。systemPath元素可以引用环境变量

<systemPath>${java.home}/lib/rt.jar</systemPath>
  • import :导入依赖范围

  • optional:标记依赖是否可选。boolean类型

  • exclusions:用来排除传递依赖性

依赖的冲突调解

有以下两个问题。

问题一 ,A有如下依赖关系。

问:这一条路径上有两个不同版本的B,A是选择B (1.0) 还是B(2.0) ?

答:Maven 根据路径最近者优先原则。B (1.0) 的路径长度为3,B(2.0) 为1,Maven会选择B(2.0) 。

问题二 ,A依赖了两个项目。其中Y依赖B(2.0) ,X依赖B (1.0)。

问:A选择哪个版本的B?

答: A会根据X和Y的定义顺序来选择。

可选依赖

假设有如下依赖关系:

牛肉面里面有面条,面条可以通过面粉或荞麦制作,所以面粉和荞麦是可选项。

但在制作面的时候,只需要一种面粉(假如)。那么就需要选择一种面粉

当面(面条)使用两种不同的面粉,需要在POM中声明:

<dependency>
...小麦面
<optional>true</optional>
</dependency>
<dependency>
...荞麦面
<optional>true</optional>
</dependency>

当牛肉面需要使用面的时候就需要在Maven中声明

<dependency>
<!-- 面 -->
</dependency>
<dependency>
<!-- 面粉 -->
</dependency>

在上面的例子中,最好的做法就是为小麦面粉和荞麦面粉分别创建一个maven项目,基于同样的groupId分配不同的artifactId。在各自的POM中声明对应的JDBC驱动依赖,而且不使用可选依赖,只需要根据需要来选择使用。

排除依赖

当项目A使用了项目B,但是项目B依赖的一个构建使用项目C(1.0) 由于C(1.0)的一些问题会导致本身的项目出现错误,但是项目C在新版本中解决了这个问题,那么只需要使用exclusions标签排除项目C(1.0)再依赖项目C(2.0)。如上图所示

项目A的POM:

<dependencies>
<dependency>
<groupId>....</groupId>
<artifactId>B</artifactId>
<version>1.0</version>
<exlusions>
<exlusion>
<groupId>....</groupId>
<artifactId>C</artifactId>
<version>>1.0</version>
</exlusion>
</exlusions>
</dependency>
<!-- 排除后只需要引入新版本 -->
<dependency>
<groupId>....</groupId>
<artifactId>C</artifactId>
<version>2.0</version>
</dependency>
</dependencies>

通过使用Maven属性来归类依赖

具体只需要在POM中定义:

<properties>
<project-A.version>1.0</project-A.version>
</properties>
<dependencies>
<dependency>
....
<artifactId>子项目A</artifactId>
<version>${project-A.version}</version>
</dependency>
</dependencies>

依赖优化

可以通过如下命令查看当前项目的已解析依赖(Resolved Dependency):

mvn dependency:list

当这些依赖经过Maven解析后,就会构成一个依赖书,通过这个依赖书就能看到每个依赖是通过哪条路径传入的。可以通过如下命令查看当前项目的依赖树

mvn dependency:tree

如果想知道该项目还有哪些问题,可以使用如下命令分析,算是Debug:

mvn dependency:analyze

mvn dependency:analyze 只能分析出编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖就发现不了。

下图中提示的是有两个包在该项目中未使用。这一类依赖不应该直接删除而是得仔细考虑。

仓库

任何一个依赖、插件或者项目构建的输出,都可以称为构件。

如果每个项目中都放置着一个重复log4j,这显然不好。这样做不仅仅浪费磁盘空间也难于管理。

文件的复制等操作也会降低构建速度。

Maven则在一个统一位置存储所有的Maven项目共享的构件,这个统一的位置就是仓库

由于坐标的机制,任何一个Maven项目使用任何一个构件的方式都是一样的。为了方便重用,项目构建完毕后生成的构件也可以安装或部署到仓库中,供其他项目使用。

有存储就肯定有存储方式,那肯定是通过文件系统存储了,有文件系统就肯定有文件的路径,用来标识一个文件,Maven有坐标,那么就只需要通过坐标来生成路径。

仓库的分类

maven仓库只分为两类:本地仓库和远程仓库。

当Maven根据坐标去寻找构件的时候,会先查看本地仓库。如果本地仓库没有,那么就去远程仓库下载并存储到本地仓库中。如果都没有则会报错。

本地仓库

每个用户在自己的目录下都有一个路径名为.m2/repository/ 的仓库文件。当然这个位置也可以自己定义。

通过使用Maven的配置文件 (这是用户级别的,只对当前用户可用)

Linux or macOS

 ~/.m2/settings.xml

Windows

c:\Users\用户名\.m2\settings.xml

! 需要注意的是,用户级的配置文件是不存在的,用户需要从Maven安装目录复制 maven安装目录/conf/settings.xml 文件再进行编辑。

也可以创建自己的私有构件,只需要将该项目安装到本地仓库中,做法如下:

mvn clean install

中央仓库

中央仓库是Maven自带的远程仓库,它包含大部分开源的构件。

由于最原始的本地仓库是空的,Maven必须要有一个可用的远程仓库,才能在执行Maven命令的时候下载构件。

<repositories>
<repository>
<id>central</id>
<name>Maven Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

上面这段配置是所有Maven项目都会隐式的(implicitly)继承的Super POM ,该文件包含在Maven-model-builder模块中。

快照版本

Maven中,任何一个项目可能有两种版本,发布版本和快照版本。

考虑这样一个问题。 我和同事一起开发一个项目,我负责Module A,他负责Module B。同事依赖于我的项目。在开发过程中,同事需要使用到最新的版本,用来开发和集成测试。

答案是使用快照;在上述情况下,我只需要将Module A的版本号设定为 1.0-SNAPSHOT,然后发布到内部仓库中,在发布的过程中,Maven会自动为构件打上时间戳。有了时间戳,Maven就能随时找到仓库中该构件的最新版本。

默认情况下,Maven每天检查一次更新(由仓库配置的updatePolicy配置)。也可以使用-U参数手动更新:

mvn clean install-U

快照版本很不稳定,所以只应该在内部使用。当需要发布该构件,只需要去掉SNAPSHOT 即可

项目聚合与集成

聚合

聚合就是将多个项目聚合为一个项目。如需聚合两个模块,只需要创建一个新模块,然后通过该模块构建整个项目的所有模块。

该模块需要进行如下声明

<groupId>com.xrtero</groupId>
<artifactId>hello</artifactId>
<version>1.0-SNAPSHOT</version> <modules>
<module>maven-test</module>
<module>maven-test/moduleA</module>
<module>maven-test/moduleB</module>
</modules> <packaging>pom</packaging>

对于聚合模块来说,打包方式必须声明为POM

module 标记的是被聚合模块的路径。

继承

继承这一特性能够将重复的配置抽取出来,帮助减少不必要的工作。

继承能够建立一个父子关系;子模块需要显示的声明父模块。

relativePath是可选的,因为它默认的路径是上一级,所以这里并不需要声明

<parent>
<artifactId>hello</artifactId>
<groupId>com.xrtero</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

依赖管理

依赖是可以被继承的。Maven提供的dependencManagement在父模块中定义的元素能够让子模块集成到父模块的依赖配置。又能保证子模块依赖使用的灵活性。

例如,可以用来管理Spring框架子模块的版本:

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring.version>3.1.3.RELEASE</spring.version>
</properties>

org.springframework
spring-core
${spring.version}

org.springframework
spring-beans
${spring.version}

甚至还可以将其他POM的dependencyManagement合并到项目中,需要将scope指定为importimport 的作用就是将目标POM中的dependencyManagement 配置导入并合并到当前POM的dependencyManagement中。

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

插件管理

Maven 提供了 pluginManagement 元素帮助管理插件。在该元素中配置的依赖不会造成实际的插件调用行为。 和 dependencyManagement 作用大体相似。为所有模块重复类似的插件配置使用pluginManagement是最好的办法

常用插件

Maven Archetype

Archetype 是一个模版工具,可以通过一个模版创建一个项目,也可以通过项目创建一个原型(Archetype)

可以通过如下命令创建一个项目,该命令选择的是

maven-archetype-quckstart插件,当然还有其他插件可供选择

mvn archetype:generate -DgroupId=your_group_id -DartifactId=your_artifact_id -DarchetypeArtifactId=maven-archetype-quickstart

有待更新

一些其他用法

Root POM中的占位符

通常用在多模块的项目中,如使用shade插件时需要指定一些参数,这些参数每个模块都不一样。

root pom

<properties>
这里是占位符
<class-name/>
</properties> .... 省略了 <build>
<pluginManagement>
<plugins>
<plugin>
<!-- 当该插件需要指定主类时,直接使用<class-name/> -->
<class-name>${class-name}</class-name>
</plugin>
</plugins>
</pluginManagement>
</build> Module 1 pom 以及其他 <properties> <!-- 定义子模块的class name --> <class-name> you class name </class-name> </properties>
  <plugins>  

      <plugin>  

     </plugin>  

</plugins>

Maven3 入门到入门的更多相关文章

  1. babel从入门到入门

    babel从入门到入门 来源 http://www.cnblogs.com/gg1234/p/7168750.html 博客讲解内容如下: 1.babel是什么 2.javascript制作规范 3. ...

  2. Android视频录制从不入门到入门系列教程(一)————简介

    一.WHY Android SDK提供了MediaRecorder帮助开发者进行视频的录制,不过这个类很鸡肋,实际项目中应该很少用到它,最大的原因我觉得莫过于其输出的视频分辨率太有限了,满足不了项目的 ...

  3. Android视频录制从不入门到入门系列教程(三)————视频方向

    运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子 ...

  4. springboot + kafka 入门实例 入门demo

    springboot + kafka 入门实例 入门demo 版本说明 springboot版本:2.3.3.RELEASE kakfa服务端版本:kafka_2.12-2.6.0.tgz zooke ...

  5. springboot + mybatisPlus 入门实例 入门demo

    springboot + mybatisPlus 入门实例 入门demo 使用mybatisPlus的优势 集成mybatisplus后,简单的CRUD就不用写了,如果没有特别的sql,就可以不用ma ...

  6. Maven3.x 插件开发入门

    Maven工具有很多插件,各种各样的插件,让我们开发调试过程中非常方便,但是终究是有你想要的但是现目前插件不能满足的(可能性非常非常低),这个时候就需要使用其他的替代工具,或者是自己来开发一个Mave ...

  7. spring入门--spring入门案例

    spring是一个框架,这个框架可以干很多很多的事情.感觉特别吊.但是,对于初学者来说,很难理解spring到底是干什么的.我刚开始的时候也不懂,后来就跟着敲,在后来虽然懂了,但是依然说不明白它到底是 ...

  8. 《图说VR入门》——入门汇总

    本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/53818922 作者:car ...

  9. 09Vue.js快速入门-Vue入门之Vuex实战

    9.1. 引言 Vue组件化做的确实非常彻底,它独有的vue单文件组件也是做的非常有特色.组件化的同时带来的是:组件之间的数据共享和通信的难题. 尤其Vue组件设计的就是,父组件通过子组件的prop进 ...

随机推荐

  1. XCTF练习题---MISC---give_you_flag

    XCTF练习题---MISC---give_you_flag flag:flag{e7d478cf6b915f50ab1277f78502a2c5} 解题步骤: 1.观察题目,下载附件 2.打开发现是 ...

  2. py文件加密打包成exe文件

    python的py.pyc.pyo.pyd文件区别 py是源文件: pyc是源文件编译后的文件: pyo是源文件优化编译后的文件: pyd是其他语言写的python库: 为什么选用Cpython .p ...

  3. SSH 证书登录教程

    开源Linux 专注分享开源技术知识 SSH 是服务器登录工具,提供密码登录和密钥登录. 但是,SSH 还有第三种登录方法,那就是证书登录.很多情况下,它是更合理.更安全的登录方法,本文就介绍这种登录 ...

  4. Linux强制用户首次登录修改密码

    一个执着于技术的公众号 地方 前言 Linux强制用户首次登陆修改密码,这应该是RHCE认证中用户管理部分, 属于很基础的内容了.可是我忘记了,所以就有了下面的记录~ 实验过程 1.创建用户并设置登录 ...

  5. 『现学现忘』Git基础 — 21、git diff命令

    目录 1.git diff 命令说明 2.比较工作区与暂存区中文件的差别 3.比较暂存区与本地库中文件的差别 4.总结git diff命令常见用法 5.总结 1.git diff 命令说明 在comm ...

  6. 羽夏 Bash 简明教程(下)

    写在前面   该文章根据 the unix workbench 中的 Bash Programming 进行汉化处理并作出自己的整理,并参考 Bash 脚本教程 和 BashPitfalls 相关内容 ...

  7. Ajax——Get请求

    Get.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  8. Elasticsearch(es)介绍与安装

    ### RabbitMQ从入门到集群架构: https://zhuanlan.zhihu.com/p/375157411 可靠性高 ### Kafka从入门到精通: https://zhuanlan. ...

  9. MTK 虚拟 sensor bring up (pick up) sensor1.0

    pick up bring up sensor1.0 1.pick up对比 2.SCP 1.添加驱动文件 2.添加编译环境(打开开关) 注:编译过程中如果显示内存不够 3.修改底层数据上报方式 3. ...

  10. Mysql优化基础之Explain工具

    字段解释 id:代表sql中查询语句的序列号,序列号越大则执行的优先级越高,序号一样谁在前谁先执行.id为null则最后执行 select_type:查询类型,表示当前被分析的sql语句的查询的复杂度 ...