Maven详解
MavenMaven简介介绍Maven前,我们要先弄清楚什么是构建?我们工作时,除了编写源代码,每天有一部分时间都花在了编译、运行单元测试、生成文档、打包和部署上面,这就是构建。多次重复的做这种工作,浪费了大量程序员的时间,所以有人用软件使这一系列操作完全自动化,只需要一条命令,这些繁琐的操作会很快完成。强大的MavenMaven是基于项目对象模型(POM),可以通过一小段描述信息来管...
Maven
Maven简介
介绍Maven前,我们要先弄清楚什么是构建?
我们工作时,除了编写源代码,每天有一部分时间都花在了编译、运行单元测试、生成文档、打包和部署上面,这就是构建。多次重复的做这种工作,浪费了大量程序员的时间,所以有人用软件使这一系列操作完全自动化,只需要一条命令,这些繁琐的操作会很快完成。
强大的Maven
Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。是一个异常强大的构建工具,能帮我们自动化构建过程,从编译、运行单元测试、生成文档、打包和部署,我们只需要输入见得的Maven命令即可。
Maven是java编写的,因此可以跨平台。总的来说,Maven 简化了工程的构建过程,并对其标准化。它无缝衔接了编译、发布、文档生成、团队合作和其他任务。Maven 提高了重用性,负责了大部分构建相关的任务
Maven不仅仅是构建工具,还是依赖管理工具和项目信息管理工具,还有一个强大的功能就是能够自动下载项目依赖的第三方库,Maven通过坐标来准确的定位到每一个jar包,并自动帮我们下载。
Maven的安装和配置
1.先从官网下载Maven,我这里下载的最新版 apache-maven-3.5.0-bin
- 1
2.下载成功后要配置Maven环境变量
- 1
3.测试是否安装成功,在系统命令行中执行命令:mvn –v
- 1
4.在Maven安装的目录的conf下有settings.xml文件,这是Maven的全局配置文件
我们可以在这儿配置本地仓库的地址,仓库后面会详解,里面会存放Maven下载的插件等
仓库地址默认系统的用户目录下的.m2/repository
我们自己来改变一下,当然我们
- 1
- 2
- 3
- 4
- 5
5.我们先运行一条简单的命令 mvn help:system,该命令会打印出所有的java系统属性和环境变量。
运行这条命令主要目的是第一次运行Maven命令时,会自动下载Maven的插件,包括pom文件和jar,都被下载到了本地仓库中。
- 1
- 2
简单的Maven入门项目
安装好Maven后,我们来手动搭建一个简单的Maven项目,Maven项目是约定大于配置,有固定的结构。
Maven项目的结构
Project
|--src(源码包)
|--main(正常的源码包)
|--java(.java文件的目录)
|--resources(资源文件的目录)
|--test(测试的源码包)
|--java
|--resources
|--target(class文件、报告等信息存储的地方)
|--pom.xml(maven工程的描述文件)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
1.我们创建一个HelloWorld的项目,按照Maven项目的结构创建
只需创建src和pom.xml即可,target文件夹Maven会帮我们生成
- 1
- 2
2.我们来编写一下pom.xml ,Maven的核心是pom.xml,pom定义了项目的基本信息,描述项目如何构建,声明项目依赖等。
<?xml version="1.0" encoding="UTF-8"?>
<!--project是pom文件根元素,声明了一些命名空间-->
<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">
<!-- 指定了当前POM模型的版本,只能是4.0.0 -->
<modelVersion>4.0.0</modelVersion>
<!-- groupId 组织名称,定义了项目属于哪个组 -->
<groupId>com.cad.maven</groupId>
<!-- artifactId,定义了当前项目在组中的唯一标识。一般是项目名称 -->
<artifactId>HelloWorld</artifactId>
<!-- 当前项目版本号:每个工程发布后可以发布多个版本,依赖时调取不同的版本,使用不同的版本号 -->
<version>0.0.1</version>
<!-- 名称:可省略,提供一个更友好的名称 -->
<name>Hello</name>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
3.创建主代码HelloWorld.java,放在src/main/java 下面 ,该类的包名和类名应该和pom中定义的groupId和
artifactId一样
package com.cad.maven;
public class HelloWorld{
public String sayHello(String name){
return "hello"+name;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
4.使用Maven进行编译,在项目根目录下使用命令 mvn -compile
第一次编译还是会下载很多插件,jar包等
编译成功后会在项目根目录下生成target目录,里面包含了.class文件和一些信息
- 1
- 2
- 3
- 4
编写测试代码
1.测试代码按照约定应该位于src/test/java 路径下,我们使用我们熟悉的junit来进行测试,但是我们没有jar包,这时候我们可以使用maven为我们添加一个jUnit依赖
<?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/xsd/maven-4.0.0.xsd">
<!-- 版本:4.0.0 -->
<modelVersion>4.0.0</modelVersion>
<!-- 组织名称:暂时使用 组织名称+项目名称 作为组织名称 -->
<!-- 组织名称:实际名称 按照访问路径规范设置,通常以功能作为名称:eg: junit spring -->
<groupId>com.cad.maven</groupId>
<!-- 项目名称 -->
<artifactId>HelloWorld</artifactId>
<!-- 当前项目版本号:同一个项目开发过程中可以发布多个版本,此处标示0.0.1版 -->
<!-- 当前项目版本号:每个工程发布后可以发布多个版本,依赖时调取不同的版本,使用不同的版本号 -->
<version>0.0.1</version>
<!-- 名称:可省略 -->
<name>Hello</name>
<!-- 依赖关系 -->
<dependencies>
<!-- 依赖设置 -->
<dependency>
<!-- 依赖组织名称 -->
<groupId>junit</groupId>
<!-- 依赖项目名称 -->
<artifactId>junit</artifactId>
<!-- 依赖版本名称 -->
<version>4.9</version>
<!-- 依赖范围:test包下依赖该设置 -->
<scope>test</scope>
</dependency>
</dependencies>
</project>
Maven会自动访问中央仓库下载依赖的jar包。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
2.编写测试类,使用 Assert断言我们的结果是否为hellomaven
package com.cad.maven;
import static org.junit.Assert.*;
import org.junit.Test;
public class TestHelloWorld{
@Test
public void testsayHello(){
HelloWorld hw=new HelloWorld();
String result=hm.sayHello("maven");
AssertEquals("hellomaven",result);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
3.运行mvn clean test命令
会显示测试报告,通过了多少、失败了多少、跳过了多少等
- 1
- 2
- 3
项目打包
将我们的项目打包,包可以指定很多类型,后面我们会详细介绍。不指定的话默认是jar包,
我们使用mvn package命令进行打包,会在target目录下有我们打好的jar包
- 1
- 2
项目安装
我们其他的Maven项目如何使用我们的项目呢,我们可以使用install命令将我们的jar包安装到本地仓库,然后在其他项目中通过gav来引用。
mvn install
使用Archetype生成项目骨架
Maven项目有一些约定,在根目录放pom.xml文件,src/main/java中放源码等等。如果我们每次创建Maven项目都需要自己来创建这个骨架的话,估计会烦的要命。Maven为我们提供了Archetype来帮助我们快速构建项目骨架。
我们在使用mvn archetype:generate命令生成项目骨架,Maven会有很多骨架供我们选择,也会提供一个默认的骨架,我们直接回车,然后会让我们输入groupid、artifactid、version、package等,然后就可以生成完整的项目骨架,极其方便。
常用Maven命令总结
Maven的命令要在根在目录中去执行
Mvn compile: 编译命令
Mvn clean: 清除命令,清除已经编译好的class文件,具体说清除的是target目录中的文件
Mvn test: 测试命令,该命令会将test目录中的源码进行编译
Mvn package: 打包命令
Mvn install: 安装命令,会将打好的包,安装到本地仓库
命令是可以组合的:
Mvn clean compile:先清空再编译
mvn clean test命令:先执行clean,再执行test,通常应用于测试环节
mvn clean package:先执行clean,再执行package,将项目打包,通常应用于发布前
执行过程:
清理————清空环境
编译————编译源码
测试————测试源码
打包————将编译的非测试类打包
mvn clean install:先执行clean,再执行install,将项目打包,通常应用于发布前
执行过程:
清理————清空环境
编译————编译源码
测试————测试源码
打包————将编译的非测试类打包
部署————将打好的包发布到资源仓库中
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
M2Eclipse的使用
我们的项目一般都是在IDE中进行编写,Eclipse有一款强大的Maven插件M2Eclipse。新版的Eclipse中都自带这款插件。
1.我们在Eclipse中创建一个Maven项目,选择new->Maven Project,选择默认的工作空间即可,点击next
- 1
2.这一步会让我们选择骨架,我们选择quickstart
- 1
3.输入坐标
- 1
4.点击finish,完成创建
- 1
执行Maven命令
我们在项目上右键run as会出现一些常用的Maven命令。
如果这些命令不能满足我们要求,我们需要别的命令怎么办呢?我们只需要选择Maven build就可以自己输入Maven命令,在这里输入Maven命令不需要加mvn。点击run即可运行
Maven坐标
Maven的很强大的一个功能就是管理项目依赖,为了能自动化的精确获取到项目依赖,Maven就必须为其唯一标识,Maven通过坐标来构建唯一标识。
平面几何中,使用(x,y)坐标来表示平面中的任何一个点。Maven中拥有数量巨大的构件,就是jar、war等文件,Maven自己创建了一种坐标规则,只要我们指定争取的坐标,Maven就会到中央仓库中下载这些构建,Maven的中央仓库中包含了世界上流行的大部分构件。
坐标详解
我们来看一组坐标
<groupId>com.cad.maven</groupId>
<artifactId>HelloWorld</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
- 1
- 2
- 3
- 4
- 5
- 6
groupId:组织名称。项目属于哪个组。例如Spring-core,Spring-context都是属于SpringFramework组的。
artifactId:项目名称。
version:定义当前项目的版本
packaging:定义项目的打包方式。例如jar、war。不指定默认为jar
Maven依赖管理
根元素project下的dependencies可以包含一个或多个 dependency元素,以声明一个或多个项目依赖。
每个依赖可以包含以下元素。
<groupId>:依赖组织名称
<artifactId>:依赖项目名称
<version>:版本
<scope>:依赖范围
<type>:依赖类型,对应于项目坐标定义的packing。默认值为jar
<optional>:标记依赖是否可选
<exclusions>:用来排除传递性依赖
- 1
- 2
- 3
- 4
- 5
- 6
- 7
依赖范围
我们在前面的测试例子中,依赖的JUnit设置了一个<scope>为test,为什么这么设置呢?这就涉及到依赖范围的概念。
首先我们需要知道主项目代码在编译时需要使用一套classpath,编译主代码需要使用jar包,该jar包就以依赖的方式被引入到classpath中。Maven在编译和执行测试代码时,会使用另一套classpath,然后例如junit包等也会被引入这个classpath中。最后,实际运行项目时,又会使用另一套classpath,一些spring等jar包需要在该classpath中,而junit等就不需要。
- 1
- 2
- 3
依赖范围就是用来控制依赖应该在何时存在,例如junit包只需要在测试时存在,项目正式运行时就不需要了。
Maven有以下几种依赖范围
compile:没有指定,默认使用该依赖范围。使用此依赖范围,对于主代码、测试、运行时都会被引入各自的classpath。例子是spring-core,在主代码、测试、运行时都需要这个依赖。
test:测试依赖范围。使用此依赖范围的Maven依赖,只会被测试classpath引入。例子是JUnit,只有在测试时需要。
provided:使用此依赖范围的Maven依赖,会被主代码、测试等classpath引入,在运行时无效。例子是servlet-api,编译主代码和测试时需要,但在运行时由于容器会提供,所以可能会出现冲突,就不需要Maven再引入依赖。
runtime:运行时依赖范围。使用此依赖范围的Maven依赖,只有项目运行时会引入依赖。例子是JDBC驱动,在编译主代码时无效,只有测试运行还有运行时需要引入。
依赖传递
什么是依赖传递呢?
我们说一个简单的例子,例如我们的B项目依赖C项目,我们的A项目依赖于B项目,C项目的很多依赖在A项目中也需要,Maven有依赖传递机制,我们A项目既然依赖了B项目,就不需要考虑B项目依赖了什么,也不用担心使用不了C项目的依赖,再重复定义依赖,Maven会解析各个直接依赖的pom文件,将那些间接依赖引入到当前项目中。
- 1
依赖传递范围
依赖范围会对依赖传递产生影响。
我们上面的例子中,A项目依赖B项目,在A项目的pom文件中设置B项目的依赖范围为compile,B项目依赖C项目,在B项目的pom文件中设置c项目的依赖范围为compile。那么A项目对于C项目的传递性依赖的范围就是compile。
A项目依赖于B项目,B项目依赖于C项目。我们说A对B是第一直接依赖,B对C是第二直接依赖,A对C是间接依赖。
- 1
- 2
- 3
- 4
第一直接依赖的范围和第二直接依赖的范围决定了依赖传递范围。我们用下图来说明一下。
左边第一列代表第一直接依赖的范围,上面第一行代表第二依赖的范围,中间表示传递依赖范围。
- 1
- 2
我们再来举个例子来理解该表,例如我们的A项目依赖Spring-core.jar,这是第一直接依赖,其依赖范围是test。而Spring-core.jar又依赖于commons-logging.jar,这是第二直接依赖,其依赖范围是compile。因此,对照表,即可得知,A项目是commons-logging.jar的一个范围是test的传递性依赖,也就是说在测试的时候才会将commons-logging.jar引入。我们来验证一下
- 1
1.我们创建第一个项目FirstMaven
主代码:
package com.cad.maven.FirstMaven;
public class FirstMaven
{
public String sayHello(String name) {
return "hello"+name;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
pom文件:
<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>
<groupId>com.cad.maven</groupId>
<artifactId>FirstMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>FirstMaven</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
2.创建第二个项目,SecondMaven,我们在pom文件中依赖第一个项目,依赖范围设置为compile,由于第一个项目中依赖了junit,并且范围设置为compile。所以第二个项目中也自动依赖了junit。
主代码
package com.cad.maven.SecondMaven;
import com.cad.maven.FirstMaven.FirstMaven;
class SecondMaven
{
public String sayHello(String name) {
FirstMaven fm=new FirstMaven();
return fm.sayHello(name);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
pom文件
<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>
<groupId>com.cad.maven</groupId>
<artifactId>SecondMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SecondMaven</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.cad.maven</groupId>
<artifactId>FirstMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
3.我们创建第三个项目,ThirdMaven,依赖第二个项目,并且依赖范围设置为test
public class ThirdMaven
{
public static void main( String[] args )
{
ThirdMaven tm=new ThirdMaven();
String result=tm.sayHello();
System.out.println(result);
}
public String sayHello() {
return new SecondMaven().sayHello("maven");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
pom文件
<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>
<groupId>com.cad.maven</groupId>
<artifactId>ThirdMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ThirdMaven</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.cad.maven</groupId>
<artifactId>SecondMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
4.这时候我们的依赖传递范围为test,就说明只在测试时能引入,所以这时候在主代码运行会报错,而测试时候运行则不会报错。
- 1
总结:
当第二依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
当第二直接依赖的范围是test的时候,依赖不会得以传递。
当第二依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为 provided;
当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递的依赖范围为runtime;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
依赖冲突
什么是依赖冲突呢?
比如我们A项目依赖B项目,B项目依赖C项目。B项目依赖JUnit3.0版本,C项目依赖JUnit4.0版本,并且依赖范围都是compile。那么这时候就出现了冲突,A项目会依赖哪个呢?
Maven使用了就近原则,会使用距离A项目近的版本,所以会使用B项目的JUnit3.0版本。
如果一个pom文件中引入了两个不同版本的相同依赖,那么越靠近pom文件下面的越近。
- 1
- 2
- 3
- 4
- 5
可选依赖
什么是可选依赖呢?
比如我们A项目依赖B项目,B项目依赖C项目,设置的依赖范围都是compile。但是我们的A项目根本就不需要C项目,这时候就可以设置C项目的可选依赖,有个 <optional></optional>标签,设置为true则依赖不会传递,设置为false,依赖继续传递,这就是可选依赖。我们把C项目的可选依赖设置为true,A项目中就不会再引用C项目。
- 1
排除依赖
我们的可选依赖可以让某个依赖不再传递,但这时就出现了一种情况,我们D依赖A项目,但是D项目也需要C,这时候C项目传递不过来,就产生了很多麻烦。这时候可以使用排除依赖,排除依赖可以把自己不需要的依赖给排除。
我们在C项目排除A项目
<dependencies>
<dependency>
<groupId>com.cad.maven</groupId>
<artifactId>SecondMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
//排除A项目
<exclusions>
<exclusion>
<groupId>com.cad.maven</groupId>
<artifactId>FirstMaven</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
归类依赖
我们的项目依赖spring,Spring项目分为很多个模块,例如Spring-core-4.0.jar,Spring-bean-4.0.jar等等。 等到我们项目需要更新Spring的时候,由于版本号都一样,所以我们是否可以将版本号给用一个常量存放,然后更新Spring时只需要改变常量的值即可。这样可以省去很多时间。
- 1
<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>
<groupId>com.cad.maven</groupId>
<artifactId>ThirdMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ThirdMaven</name>
<url>http://maven.apache.org</url>
<properties>
<springframework.version>4.5</springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
</dependencies>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
优化依赖
通过前面的学习了解,Maven会自动解析所有直接依赖和传递依赖,并且判断依赖范围,最后项目得到的的依赖被称为已解析依赖。
我们可以通过mvn dependency:list来查看当前项目已解析依赖并且显示其范围
- 1
我们可以通过mvn dependency:tree命令来查看当前项目依赖树
- 1
Maven仓库
Maven可以在某个统一的位置存储所有项目的依赖,这个位置就是仓库。Maven项目中只需要声明依赖的坐标,Maven会根据坐标自动寻找仓库中的依赖,并使用他们。而我们的项目也可以安装部署到仓库中,使其他项目使用。
根据Maven坐标定义每个构建在仓库中唯一存储路径大致为:groupId/artifactId/version/artifactId-version.packaging。
仓库的分类
Maven仓库分为本地仓库和远程仓库。Maven根据坐标寻找依赖时,会先到本地仓库查找,如果有则直接使用,如果没有则到远程仓库查找,如果查找到则下载到本地仓库使用,没有查找到就报错。
远程仓库又分为中央仓库和私服还有其他公开的远程仓库。私服是一种特殊的远程仓库,我们可以在公司内部部署一个私服,这样每个员工使用依赖时就不用每个人都去中央仓库下载,而是去局域网的私服下载,还可以把公司内部的项目放在私服上,供所有人使用,节省了带宽和时间。
本地仓库
默认情况下,本地仓库的地址是用户目录下的.m2/repository/ ,但是我们可以编辑settings.xml文件来自己设置目录,前面我们有讲过,就不再多说。
中央仓库
默认的中央仓库地址是http://repo1.maven.org/maven2。中央仓库包含了世界上大部分开源依赖。
私服
私服架设在局域网内,供局域网内的用户使用。Maven需要下载依赖时,先请求私服,如果私服没有,则从外部的远程仓库下载,自己还可以向私服上上传自己的项目,供局域网使用。
私服有很多优点。
- 节省自己带宽:大量对于外部仓库的访问会占用很多带宽。
- 没网的时候也可以使用
- 可以部署自己的项目
后面我们会详细介绍私服的搭建。
配置其他远程仓库
有时候我们默认的中央仓库无法满足我们的需求,可能项目需要的依赖存在于其他公开的远程仓库。例如JBoss Maven仓库,可以在我们的pom文件中配置该仓库。
配置JBoss Maven仓库
<repositories>
<repository>
<!--唯一id-->
<id>jboss</id>
<name>JBoss Repository</name>
<!--仓库的url地址-->
<url>http://repository.jboss.com/maven2/</url>
<!--开启发布版本下载支持-->
<releases>
<enabled>true</enabled>
</releases>
<!--关闭快照版本下载支持-->
<snapshots>
<enabled>false</enabled>
</snapshots>
<!--使用默认布局-->
<layout>default</layout>
</repository>
</repositories>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
快照版本
Maven中任何一个项目依赖都需要有唯一的版本。例如1.0, 2.3.1 , 4.2-SNAPSHOT.
其中1.0,2.3.1都是发布版本,4.2-SNAPSHOT是快照版本。
为什么要区别发布版本和快照版本呢?
A开发一个项目的B模块,C开发D模块,D模块依赖B模块。A需要经常将自己的B模块更新供D使用。Maven每个坐标都是唯一的,A将自己的项目发布为2.1版本供C调用,那么有一些微小的改动,就需要发布2.2,2.3等版本,造成版本号的滥用。Maven的快照就是解决这个问题,A只需要将自己的版本设置为2.1-SNAPSHOT,发布到私服中,Maven会自动为项目打上时间戳,有了时间戳,Maven就能找到该项目的最新版本,C开发D模块时,D模块会自动的检查模块B的2.1-SNAPSHOT是否是最新,发现有更新便下载。
快照版本应该只在组织内部或模块依赖间使用,真正发布还是要发布正式版。
Maven生命周期
什么是生命周期?
Maven还没出现之前,项目构建的生命周期就存在。程序员每天都对项目进行清理、编译、测试、打包等工作,各个公司都一样,有的是手工完成,有的可以编写自动化脚本完成,虽然过程可能有点细微的不一样,但非常类似。Maven的生命周期就是对所有的构建过程进行抽象和统一,总结了一套高度完善、易扩展的生命周期,包含了清理、编译、测试、打包、部署等几乎所有的构建步骤。Maven的生命周期是抽象的,不做任何事情,都由各种插件完成。简单的说,生命周期可以理解为构建步骤的集合。
Maven有3套生命周期,clean、default、site。生命周期包括多个阶段,这些阶段有一定的顺序,并且执行后面的阶段前面的阶段也会自动执行。例如clean的生命周期包含pre-clean、clean、post-clean,当用户执行pre-clean命令时,只有pre-clean执行,当用户执行clean时,会执行pre-clean和clean命令,当用户执行post-clean命令时,会执行三个命令。
clean生命周期
- pre-clean:执行一些清理前需要完成的工作
- clean:清理上一次构建生成的文件
- post-clean:执行一些清理后需要完成的工作
default生命周期
validate
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 接受编译好的代码,打包成可发布的格式,如 JAR 。
pre-integration-test
integration-test
post-integration-test
verify
install 将包安装至本地仓库,以让其它项目依赖。
deploy 将最终的包复制到远程的仓库,以让其它开发人员与项目共享。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
site生命周期
site生命周期的目的是建立发布项目站点,上面有项目的各种信息。
pre-site 执行一些需要在生成站点文档之前完成的工作
site 生成项目的站点文档
post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
site-deploy 将生成的站点文档部署到特定的服务器上
- 1
- 2
- 3
- 4
Maven插件
我们前面说Maven的核心仅仅定义了抽象的生命周期,具体的任务是由插件完成的。为了让用户不用自己配置插件就能构建Maven项目,Maven在核心为一些主要的生命周期绑定了很多插件,当用户执行生命周期阶段时,对应的插件就会起作用。
自定义绑定插件
<build>
<plugins>
<!--指定编译插件用的jdk版本-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
插件的使用是Maven中很难的一个地方,如果需要深入可以去参考文档。
Maven聚合
在大型项目中,可能会有很多模块,在Maven中可以将这些模块给聚合成一个项目。
1.我们首先需要创建一个空的Maven项目,来去聚合其他模块,该模块的packaging必须为pom
- 1
2.通过<module>标签聚合其他项目,该被聚合的项目路径必须放在聚合项目pom文件的同一路径下
<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>
<groupId>com.cad.MavenWeb</groupId>
<artifactId>WebDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>WebServlet</module>
<module>WebService</module>
</modules>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
我们可以直接使用Eclipse图形化创建聚合项目
1.先创建一个Maven项目,将packaging改为pom
2.新建模块即可
- 1
- 2
- 3
3.我们执行一下编译命令,Maven会首先解析聚合模块的pom,分析需要构建的模块,然后根据顺序构建各个模块
- 1
- 2
Maven继承
Maven的继承,实质上指的是pom文件的继承,我们在前面的例子中会发现,pom文件中有的时候会有很多重复的内容,例如重复引入插件,重复指定gav坐标等。
1.我们首先创建一个父项目,父项目的打包方式必须为pom
由于父项目只是为了让子 项目继承pom文件,所以打包方式选为pom。
- 1
- 2
2.使用Eclipse创建子项目,只需要指定父项目的坐标即可
- 1
我们看一下子项目的pom文件 ,可以看到用一个parent标签引入了父项目
<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>
<parent>
<groupId>com.cad.maven</groupId>
<artifactId>ParentProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.cad.maven</groupId>
<artifactId>ChildProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
依赖继承
子项目会默认继承父项目中的依赖。但是如果某个模块继承父项目,根本不需要这些依赖,怎么办?
Maven提供的<dependencyManagement> 标签既能让子模块继承父模块的依赖,也能保证子模块依赖使用的灵活性。<dependencyManagement> 下的依赖声明并不会引入实际的依赖,只是管理依赖的版本。
- 1
例如 ,我们的父模块使用这个标签,这样即不会为父模块添加依赖,也不会为继承的子模块添加依赖
<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>
<groupId>com.cad.maven</groupId>
<artifactId>ParentProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
我们的子模块的pom文件,我们只需要给出groupId,artifactId即可,版本继承父模块的,
简化了我们一些操作
<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>
<parent>
<groupId>com.cad.maven</groupId>
<artifactId>ParentProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.cad.maven</groupId>
<artifactId>ChildProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
插件继承
同样的,Maven提供了<pluginManagement>来管理插件,和依赖继承一样,在这里面配置的插件并不存在实际的插件调用,等到子pom中配置了plugin元素时,才会真正调用。
- 1
使用Nexus创建私服
1.首先我们在官网下载Nexus, http://nexus.sonatype.org/
- 1
2.修改 \nexus-3.4.0-02\etc\nexus-default.properties文件,修改访问私服的地址和端口
- 1
3.我们在\nexus-3.4.0-02\bin 目录下执行窗口cmd命令,nexus.exe/run
执行成功后,即可访问
- 1
- 2
4.我们输入 127.0.0.1:8081\即可访问私服
- 1
5.默认的访问Nexus都是匿名的,匿名用户仅包含一些基本的权限,要全面学习Nexus,必须以管理员身份登录。
Nexus管理员默认的账号密码是 admin/admin123
- 1
- 2
Nexus仓库和仓库组
Nexus包含了各种类型的仓库概念,包括代理仓库、宿主仓库、仓库组等,每一种仓库都提供了丰富实用的配置参数。
仓库有四种类型,group(仓库组),hosted(宿主),proxy(代理),virtual(虚拟)。仓库的格式为Maven2.仓库还有一个属性Policy(策略),表示该仓库是发布Release版本仓库还是Snapshot快照仓库。
Nexus内部的几种Maven仓库
1)maven-central:代理maven中央库,默认从https://repo1.maven.org/maven2/拉取jar
2)maven-releases:一个策略为Release的宿主类型仓库,用来部署组织内部的发布版本构件
3)maven-snapshots:一个策略为Snapshot的宿主类型仓库,用来部署组织内部的快照版本构件
4)maven-public:仓库分组,把上面三个仓库组合在一起对外提供服务,在本地maven基础配置settings.xml中使用。
Maven可以直接从宿主仓库下载依赖,Maven也可以从代理仓库中下载依赖,代理仓库会间接的从远程仓库下载依赖并缓存。仓库组只是一组仓库,它会转向其组内的仓库获取依赖。
- 1
- 2
- 3
- 4
- 5
- 6
私服的配置可以自行百度,网上很多资料。
更多推荐
所有评论(0)