Maven 小结
Maven 小结
部分转载于:Apache Maven 入门篇 by George Ma
1 Apache Maven 是做什么用的?
- Maven 是一个项目管理和构建自动化工具。但是对于我们程序员来说,我们最关心的是它的项目构建功能。
- Maven 使用惯例优于配置的原则 。它要求在没有定制之前,所有的项目都有如下的结构:
目录 | 目的 |
---|---|
${basedir} | 存放 pom.xml和所有的子目录 |
${basedir}/src/main/java | 项目的 java源代码 |
${basedir}/src/main/resources | 项目的资源,比如说 property文件 |
${basedir}/src/test/java | 项目的测试类,比如说 JUnit代码 |
${basedir}/src/test/resources | 测试使用的资源 |
- 一个
maven
项目在默认情况下会产生JAR
文件-
mvn package
后建立新目录target/ - 同时,编译后的
JAR
文件会放在 ${basedir}/target/下 - 编译后的
classes
会放在${basedir}/target/classes 下面 - 测试
class
文件放在 target/test-classes/ 目录下面。
-
- maven命令:
- compile:编译,将java源文件编译成class文件
- test:测试项目,执行test目录下的测试用例
- package:打包,将项目打成jar包
- clean:删除target文件夹
- install:安装将当前项目放到Maven的本地仓库中。供其他项目使用
2 Maven的核心概念
- POM (Project Object Model)
- Maven 插件
- Maven 生命周期
- Maven 依赖管理
- Maven 库
POM(Project Object Model)
-
项目对象模型,pom.xml是整个maven项目的灵魂。
-
一个项目所有的配置都放置在
POM
文件中:定义项目的类型、名字,管理依赖关系,定制插件的行为等等。比如说,你可以配置 compiler 插件让它使用 java 1.5 来编译。 -
在
POM
中,groupId
,artifactId, packaging, version
叫作maven
坐标,它能唯一的确定一个项目。有了 maven 坐标,我们就可以用它来指定我们的项目所依赖的其他项目,插件,或者父项目。一般maven坐标
写成如下的格式:groupId:artifactId:packaging:version
<!--示例pom.xml-->
<!--project 工程的根标签。-->
<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/xsd/maven-4.0.0.xsd">
<!-- 模型版本 -->
<modelversion>4.0.0</modelversion>
<!--Maven坐标:com.mycompany.helloworld: helloworld: jar: 1.0-SNAPSHOT-->
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<groupid>com.mycompany.helloworld</groupid>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactid>helloworld</artifactid>
<!--打包的类型,有jar、war等等不同类型-->
<packaging>jar</packaging>
<!--工程的版本号。在 artifact 的仓库中,它用来区分不同的版本-->
<version>1.0-SNAPSHOT</version>
<name>helloworld</name>
<url>http://maven.apache.org</url>
<!--properties标签很重要,它定义了一些关于pom.xml的变量和其属性值-->
<!--配置依赖时,引用了这个变量,引用方法是“${junit.version}”.这种方式,在系统升级版本号、并配置父子项目(parent project)时,对我们非常有帮助,可以不用修改dependencies依赖,而直接修改properties里的值就可以了,非常方便!-->
<properties>
<project.build.sourceencoding>UTF-8</project.build.sourceencoding>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1或${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 大项目一般会分成几个子项目。在这种情况下,每个子项目就会有自己的 POM 文件,然后它们会有一个共同的父项目。这样只要构建父项目就能够构建所有的子项目了。子项目的
POM
会继承父项目的 POM。- 另外,所有的 POM都继承了一个
Super-POM。Super-POM
设置了一些默认值,比如默认的目录结构,默认的插件等等,它遵循了惯例优于配置的原则。
- 另外,所有的 POM都继承了一个
Maven插件
-
在这篇文章中,我们用了
mvn archetype:generate
命令来生成一个项目。archetype
是一个插件的名字,generate
是目标(goal)的名字。这个命令的意思是告诉maven
执行archetype
插件的generate
目标。插件目标通常会写成pluginId:goalId
-
一个目标是一个工作单元,而插件则是一个或者多个目标的集合。比如说
Jar
插件,Compiler
插件,Surefire
插件等。从看名字就能知道,Jar
插件包含建立Jar文件的目标,Compiler
插件包含编译源代码和单元测试代码的目标。Surefire
插件的话,则是运行单元测试。 -
看到这里,估计你能明白了,
mvn
本身不会做太多的事情,它不知道怎么样编译或者怎么样打包。它把构建的任务交给插件去做。插件定义了常用的构建逻辑,能够被重复利用。这样做的好处是,一旦插件有了更新,那么所有的maven
用户都能得到更新。
Maven生命周期
-
在这篇文章中,我们用的第二个命令是
:mvn package
。这里的package
是一个maven的生命周期阶段(lifecycle phase )
。生命周期指项目的构建过程,它包含了一系列的有序的阶段(phase)
,而一个阶段就是构建过程中的一个步骤。 -
那么生命周期阶段和上面说的插件目标之间是什么关系呢?插件目标可以绑定到生命周期阶段上。一个生命周期阶段可以绑定多个插件目标。当
maven
在构建过程中逐步的通过每个阶段时,会执行该阶段所有的插件目标。 -
maven 能支持不同的生命周期,但是最常用的是默认的Maven生命周期
(default Maven lifecycle )
。如果你没有对它进行任何的插件配置或者定制的话,那么上面的命令mvn package
会依次执行默认生命周期中直到包括 package 阶段前的所有阶段的插件目标:
process-resources 阶段:resources:resources
compile 阶段:compiler:compile
process-classes 阶段:(默认无目标)
process-test-resources 阶段:resources:testResources
test-compile 阶段:compiler:testCompile
test 阶段:surefire:test
prepare-package 阶段:(默认无目标)
package 阶段:jar:jar
Maven依赖管理
- 之前我们说过,
maven
坐标能够确定一个项目。换句话说,我们可以用它来解决依赖关系。在POM
中,依赖关系是在 dependencies 部分中定义的。在上面的 POM 例子中,我们用 dependencies 定义了对于junit
的依赖:
Xml 代码
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
那这个例子很简单,但是实际开发中我们会有复杂得多的依赖关系,因为被依赖的 jar
文件会有自己的依赖关系。那么我们是不是需要把那些间接依赖的 jar
文件也都定义在POM
中呢?答案是不需要,因为 maven
提供了传递依赖的特性。
- 在
POM
的 dependencies 部分中,scope
决定了依赖关系的适用范围。我们的例子中junit
的scope
是test
,那么它只会在执行compiler:testCompile and surefire:test
目标的时候才会被加到classpath
中,在执行compiler:compile
目标时是拿不到junit
的。
传递依赖 与 排除依赖
-
传递依赖:如果我们的项目引用了一个Jar包,而该Jar包又引用了其他Jar包,那么在默认情况下项目编译时,Maven会把直接引用和间接引用的Jar包都下载到本地。
-
排除依赖:如果我们只想下载直接引用的Jar包,那么需要在pom.xml中做如下配置:(将需要排除的Jar包的坐标写在中)
<exclusions> <exclusion> <groupId>cn.missbe.web.search</groupId> <artifactId>resource-search</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> </exclusion> </exclusions>
依赖范围scope
在项目发布过程中,帮助决定哪些构件被包括进来。欲知详情请参考依赖机制。
- compile :默认范围,用于编译
- provided:类似于编译,但支持你期待jdk或者容器提供,类似于classpath
- runtime: 在执行时需要使用
- test: 用于test任务时使用
- system: 需要外在提供相应的元素。通过systemPath来取得
- systemPath: 仅用于范围为system。提供相应的路径
- optional: 当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用
依赖冲突
-
若项目中多个Jar同时引用了相同的Jar时,会产生依赖冲突,但Maven采用了两种避免冲突的策略,因此在Maven中是不存在依赖冲突的。
-
短路优先
本项目——>A.jar——>B.jar——>X.jar
本项目——>C.jar——>X.jar
若本项目引用了A.jar,A.jar又引用了B.jar,B.jar又引用了X.jar,并且C.jar也引用了X.jar。在此时,Maven只会引用引用路径最短的Jar。
-
声明优先
若引用路径长度相同时,在pom.xml中谁先被声明,就使用谁。
Maven库
-
Maven远程库: 如国内镜像站
-
Maven本地库:C:\Users\username.m2\repository
-
本地库是指
maven
下载了插件或者jar
文件后存放在本地机器上的拷贝。当maven
查找需要的jar
文件时,它会先在本地库中寻找,只有在找不到的情况下,才会去远程库中找。 -
不是所有的
jar
文件都是可以从默认的远程库下载的,比如说我们自己开发的项目。-
运行下面的命令能把我们的
helloworld
项目安装到本地库:mvn install
-
一旦一个项目被安装到了本地库后,你别的项目就可以通过
maven
坐标和这个项目建立依赖关系。比如如果我现在有一个新项目需要用到helloworld
,那么在运行了上面的mvn install
命令后,我就可以如下所示来建立依赖关系:Xml 代码
<dependency> <groupid>com.mycompany.helloworld</groupid> <artifactid>helloworld</artifactid> <version>1.0-SNAPSHOT</version> </dependency>
-
-