Maven3 入门到入门
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
当执行完该执行,会依次执行validate 、compile 、test
如果不确定需要什么,首选的调用是
mvn verify
在构建环境中,可以使用下面的指令感觉的构建发布到共享存储库中
mvn clean deploy
真正做工作的还是绑定在阶段(Phase)上的插件(Plugin)目标(goal),一个阶段可以绑定零个或多个目标
插件和插件执行目标
基于 约定优先于配置(Convention Over Configuration),Maven为项目提供了定义好的生命周期和一组通用插件。这样的好处就是减少配置,不容易因为配置错误出问题。
Maven是一个以Plugin为核心的框架(或者说将主要的工作委派给了插件)。所有的工作由插件完成,一个插件有一个或多个目标,每个目标代表着插件的不同能力,这样能够最大的提升代码的复用能力。
例如,编译插件有两个目标compile和testCompile,前者源码编译,后者编译测试代码,这对应着default 生命周期的两个阶段,compile 和 test-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指定为import。 import 的作用就是将目标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 入门到入门的更多相关文章
- babel从入门到入门
		babel从入门到入门 来源 http://www.cnblogs.com/gg1234/p/7168750.html 博客讲解内容如下: 1.babel是什么 2.javascript制作规范 3. ... 
- Android视频录制从不入门到入门系列教程(一)————简介
		一.WHY Android SDK提供了MediaRecorder帮助开发者进行视频的录制,不过这个类很鸡肋,实际项目中应该很少用到它,最大的原因我觉得莫过于其输出的视频分辨率太有限了,满足不了项目的 ... 
- Android视频录制从不入门到入门系列教程(三)————视频方向
		运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子 ... 
- springboot + kafka 入门实例 入门demo
		springboot + kafka 入门实例 入门demo 版本说明 springboot版本:2.3.3.RELEASE kakfa服务端版本:kafka_2.12-2.6.0.tgz zooke ... 
- springboot + mybatisPlus 入门实例 入门demo
		springboot + mybatisPlus 入门实例 入门demo 使用mybatisPlus的优势 集成mybatisplus后,简单的CRUD就不用写了,如果没有特别的sql,就可以不用ma ... 
- Maven3.x 插件开发入门
		Maven工具有很多插件,各种各样的插件,让我们开发调试过程中非常方便,但是终究是有你想要的但是现目前插件不能满足的(可能性非常非常低),这个时候就需要使用其他的替代工具,或者是自己来开发一个Mave ... 
- spring入门--spring入门案例
		spring是一个框架,这个框架可以干很多很多的事情.感觉特别吊.但是,对于初学者来说,很难理解spring到底是干什么的.我刚开始的时候也不懂,后来就跟着敲,在后来虽然懂了,但是依然说不明白它到底是 ... 
- 《图说VR入门》——入门汇总
		本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/53818922 作者:car ... 
- 09Vue.js快速入门-Vue入门之Vuex实战
		9.1. 引言 Vue组件化做的确实非常彻底,它独有的vue单文件组件也是做的非常有特色.组件化的同时带来的是:组件之间的数据共享和通信的难题. 尤其Vue组件设计的就是,父组件通过子组件的prop进 ... 
随机推荐
- Bugku CTF练习题---社工---密码
			Bugku CTF练习题---社工---密码 flag:KEY{zs19970315} 解题步骤: 1.观察题目,思考题目要求 2.发现其中有姓名和生日这两个信息.从社工角度出发,感觉可能是名字+生日 ... 
- create-react-app的TS支持以及css模块化
			开始: 利用官方脚手架,搭建react工程.参考:https://react.docschina.org/docs/create-a-new-react-app.html. 过程: 1.暴露webpa ... 
- DH问题汇总
			本节内容主要转载于:弄清楚DL,D-H,CDH problem,CDH assumption,DDH,BDDH,BCDH. DLP(Discrete Logarithm Problem) 在乘法群\( ... 
- gol处理命令行参数 flag
			os.Args获取命令行参数 os.Args是一个srting的切片,用来存储所有的命令行参数 package main import ( "fmt" "os" ... 
- SpringBoot项目使用jasypt加解密
			Jasypt 是一个 Java 库,它允许开发者以最小的努力为他 / 她的项目添加基本的加密功能,而且不需要对密码学的工作原理有深刻的了解. 一.添加依赖 <dependency> < ... 
- Linux用户权限集中管理方案
			一.问题 服务器多,各个服务器上的管理人员多,ROOT权限泛滥,经常导致文件莫名其妙丢失,老手和新手对服务器的熟知程度不同,安全存在不稳定和操作安全隐患. 二.方案 利用sudo配置指定用户只能执行指 ... 
- 120_PowerBI堆积瀑布图_R脚本Visual
			博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.效果 二.data 三.添加字段 注意红色框标注地方 四.code # 下面用于创建数据帧并删除重复行的代码始终执行, ... 
- 安装Zabbix到Ubuntu(APT)
			运行环境 系统版本:Ubuntu 16.04.2 LTS 软件版本:Zabbix-4.0.2 硬件要求:无 安装过程 1.安装APT-Zabbix存储库 APT-Zabbix存储库由Zabbix官网提 ... 
- 关于『Markdown』:第二弹
			关于『Markdown』:第二弹 建议缩放90%食用 道家有云:一生二,二生三,三生万物 为什么我的帖子不是这样 各位打工人们! 自从我学了Markdown以来 发现 Markdown 语法真的要比 ... 
- 选择ERP频频踩雷?国内外ERP有差异,突破ERP软件单一性是关键
			信息化日新月异的蓬勃发展,导致企业在选择ERP软件时频频踩雷.企业如何选择出一个适合自己的ERP软件系统呢?是选择国外知名公司的ERP软件产品,还是选择国内性价比高的ERP软件产品呢,小编就带大家了解 ... 
