Maven官网
Maven官网地址:https://maven.apache.org/
Maven的下载、各种介绍里面都有,网上文章虽多,但专业度还是不及官网里的内容,所以一旦有什么疑难杂症搞不定,都可以到官网去查。
Maven核心概念
Maven是什么?它能帮我们做什么?
Maven是众多项目管理工具中的一个,它基于POM(project object model)即项目对象模型这种概念对项目进行管理。
具体来说,Maven主要用于管理项目的构建(build)、依赖(dependence):
- 构建指的是与项目相关的清理、编译、测试、生成报告、生成文档、打包、部署等等工作
- 依赖指的是项目实现所需的各种构件(
jar
包)
在没有项目管理工具之前,这些工作在每次做项目的时候都要重复做一遍,特别是jar包的管理,只需配置好后再用简单的指令就能自动执行它们。
最令我们Java开发者开心的莫过于Maven可以帮我们系统地管理jar
包:
- 我们只需到配置文件中配置上该项目需要什么依赖(即
jar
包),Maven就会帮我们下载到指定的仓库中,并让项目引用它 - 如果
jar
包之间有复杂的耦合(包含或版本关系),Maven会让项目引用必需且版本正确的jar
包,而不会引用多余或错误的jar
包。
Maven帮助我们从这些重复繁琐的工作中解脱出来,让我们有更多的时间精力集中于项目的开发。
仓库
Maven中的仓库(repository)所指的是一个存储jar包的地方,其中又分为远程仓库和本地仓库:
远程仓库:远程仓库通俗来说就是公共仓库,它是一个服务器,大家都可以通过网络访问这个仓库获取
其中,中央仓库(central repository)是最特殊的远程仓库,它存储了世界上所有的jar
包jar
包,世界各地的Java程序员都可以从这里获取自己想要的jar
包本地仓库:本地仓库指的就是在你的电脑硬盘中某个用于存储从远程仓库获取而来的
jar
包的目录私服:私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,提供局域网内的Maven用户使用
Maven根据坐标寻找构件,它首先会查看本地仓库是否存有该构件,如果有就直接用,如果没有,则会去远程仓库查找,找到后就把它下载到本地仓库再用,如果连远程仓库都没有,Maven会报错。
Maven遵循的原则
Maven遵循以下这个原则:
- 约定优于配置(convention over configuration)
它也被称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。
如果你以前接触过Spring、Hibernate等等框架,你应该知道这个原则具体所指是什么。
对于Maven管理项目来说,主要是对项目中的pom.xml
进行配置。如果你遵守Maven默认情况的约定,那么,无需再进行额外的配置。
最重要无疑是Maven对项目目录结构的约定:
项目核心Java源码目录(Application/Library sources):
src/main/java
与项目主体逻辑相关的Java源码文件都放到这个目录中项目核心资源目录(Application/Library resources):
src/main/resources
与项目主体逻辑相关的一些配置文件或资源文件都放到这个目录中项目测试源码目录(Test sources):
src/test/java
与项目测试相关的Java源码都放到这个目录中项目测试资源目录(Test resources):
src/test/resources
与项目测试相关的配置一些配置文件或资源文件都放到这个目录中Java Web项目资源目录(Web application sources):
src/main/webapp
开发Java Web项目时, 这也是一个重要目录,里面存放了网站的配置文件WEB-INF/web.xml
以及各种静态或动态的Web资源。
这5个目录是我们开发时最常用的,其实还有其他目录相关的约定,这就不一一列举了。
要更全面地了解可以看官方文档里描述的标准目录结构。你也可以看看本文的这一小节。
如果你是新手,在使用Maven管理你的项目时,最好是遵循这种约定来创建目录,这样Maven才能自动识别并管理这些目录。如果你不遵守这个约定,那么就得多付出一些代价(在pom.xml
中针对不遵守约定而产生变动的部分进行额外配置)。不遵守约定那就要多做一些工作那是理所当然了。这就像生活中某些约定一样,比如交通灯的约定:红灯停,绿灯行,如果你不遵守这个约定,那就得付出代价了,这个代价有可能很轻,又有可能很重。
pom.xml
文件
POM(project object model)即项目对象模型这个概念正是由pom.xml
这个配置文件具体表现出来的。pom.xml
是Maven管理项目的最最核心文件。Maven之所以能够便捷系统地对项目进行管理都是因为放置于项目目录中pom.xml
这个配置文件。
Maven通过读取该配置文件,就能使它了解到一个项目中这些关键信息:
- 项目的目录结构组成
- 项目实现所需的
jar
包
所以,我们想要用Maven更好地进行项目管理,配置pom.xml
将成为学习Maven的重点。
关于pom.xml
这个文件的更多细节,可以去看Maven官方文档。
父POM(Super POM
)
正如Java那样,java.lang.Object
是所有引用类型的父类型,而Maven也沿用了这种思想,用户所有POM
都会继承Maven中一个默认的POM
,我们可以称它为父POM
。更具体来说,即所有pom.xml
文件都会继承Maven中一个默认的pom.xml
。这个原始的pom.xml
文件我们可以在Maven目录中的lib\maven-model-builder-version.jar
这个文件找到,它名为pom-4.0.0.xml
:
1 | <!-- START SNIPPET: superpom --> |
这里面有2个关键点我们能够一眼就能看出并理解它们:
- 在
repositories
标签对中配置了下载jar
所用到的中心仓库的地址https://repo.maven.apache.org/maven2
- 在
build
标签对中配置了之前提及到的关于项目构建的众多目录约定。
当我们给自己项目的pom.xml
进行一样的配置,那么就会覆盖父pom.xml
中相同的配置,这种行为也是和面向对象的继承一样的。
下载和安装
下载
一般我们只要到官网下载页面中下载apache-maven-version-bin.zip
这个压缩包即可。
安装
首先解压
apache-maven-version-bin.zip
这个压缩包到指定目录配置系统环境变量
MAVEN_HOME
,对应的值是maven压缩包解压后的目录路径
比如:我的是解压在G盘,那么我就直接配置为G:\apache-maven-3.5.0
配置系统环境变量
PATH
,正如给JDK配置一样,配置Maven也是一样的,在PATH
后头加上值;%MAVEN_HOME%\bin
配置堆内存:再增加一个系统环境变量
MAVEN_OPTS
,其值指定为-Xms256m -Xmx512m
打开命令控制台,输入
mvn -v
验证Maven是否已经安装配置成功
注意
Maven一是个Java编写的工具,所以说在使用它之前首先要确保你的电脑安装了JRE
。
开始使用Maven
settings.xml
的起始配置
下载安装完Maven使用之前,我们都必须对settings.xml
这个配置文件进行一些设置,Maven这个工具主体设置就是在这个配置文件进行设置的。
文件位置
这个文件默认存在于2处:
- Maven安装目录下的
config
目录里 ${user.home}/.m2
目录里,即系统用户目录里的一个名为.m2
的目录
前者是全局设置,针对所有用户,而后者是用户设置,针对的是单个系统用户
在你完全没有使用过Maven的情况下,系统用户目录里的.m2
目录是不存在的,它在你首次使用Maven时才会被创建。
这2个相同的配置文件在什么时候会被应用?
- 直接在命令行使用Maven命令,那么就会应用全局配置文件,即Maven安装目录下的
config/settings.xml
- 常用的IDE:Eclipse、MyEclipse、IntelliJ IDEA默认情况下都使用用户目录里那个针对用户的配置文件,即
${user.home}/.m2/settings.xml
常用的配置项
本地仓库即存储
jar
包的目录
你在该配置文件中可以看到默认的本地仓库就在${user.home}/.m2/repository
这个目录中。
通常将它配置你所想要的目录,比如我将本地仓库指定为G
盘下的名为maven_repository
的目录。1
2
3
4
5<settings>
...
<localRepository>G:\maven_repository</localRepository>
...
</settings>各种IDE默认情况下使用的是它内置的Maven,它的默认使用的还是
.m2
目录里默认生成的setting.xml
,
而IDE在第一次打开运行时通常都会让内置的Maven下载jar
包,
所以我们最好把.m2
里也配置一下,这样不管我们使不使用IDE都好,本地仓库都是一致的。HTTP
代理
这里我以XX-Net
中的GoAgent
为例进行配置。至于XX-Net
这个翻墙软件这里就不说了,Github上有下载也有教程1
2
3
4
5
6
7
8
9
10
11
12
13<settings>
...
<proxies>
<proxy>
<id>xx-net-goagent</id>
<active>true</active>
<protocol>http</protocol>
<host>127.0.0.1</host>
<port>8087</port>
</proxy>
</proxies>
...
</settings>光配置这个,还是不够的,我们配置的是
http
代理,但在下载jar
包或maven插件时所访问的是https
地址,这就会导致有SSL
认证问题。
我在网上查询了这个问题,最后找到了答案所以,我们除了配置proxy
外,还必须进行以下这些额外的配置: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<settings>
...
<profiles>
<profile>
<id>securecentral</id>
<repositories>
<repository>
<id>central</id>
<url>http://repo1.maven.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://repo1.maven.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>securecentral</activeProfile>
</activeProfiles>
...
</settings>中心仓库都是部署在国外的,所以不同的地区或网络连接它的速度都不一样,运气不好就是十分慢了,连
jar
包都下载不到。
所以,如果你看见maven的下载jar
包的速度十分缓慢,那么你就可以配置HTTP
代理以提高对中央仓库的访问速度。镜像
如果你觉得使用代理并给Maven配置代理十分麻烦,那不妨试试配置镜像。镜像(mirror)其实指的是一个仓库,如果仓库A可以提供仓库Y存储的所有内容,那么就可以称X是Y的一个镜像。
Maven官网里给出了配置镜像的几个理由,其中一个:
There is a synchronized mirror on the internet that is geographically closer and faster
直白来说就是一个对应于某远程仓库的镜像仓库, 它在地理位置上更靠近你的地区以便于更便捷地访问比如:国内有阿里云的中央仓库,它就是官方中央仓库
repo1.maven.org/maven2
的镜像仓库。
配置如下:1
2
3
4
5
6
7
8
9
10
11
12<settings>
...
<mirrors>
<mirror>
<id>ali-maven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
...
</settings>其中,
id
、name
都可以随意,但最好是保持唯一性,url
指的就是该镜像仓库的地址mirrorOf
配置为中央仓库的id
值central
表示当前该镜像仓库是对应于中央仓库的,任何对中央仓库的请求都会转至该镜像仓库
遵循Maven约定来使用Maven管理普通Java项目
| HelloMaven
--| src
----| main
------| java 项目主体源码
------| resources 与项目主体相关的配置文件
----| test
------| java 测试源码
------| resources 与测试相关的配置文件
--| target 这个是项目编译输出目录, Maven会自动生成它,无需手工创建
--| pom.xml 项目对象模型配置文件
首先按照Maven的目录结构约定来创建Java项目目录
HelloMaven
里面有src
,src
里有main
和test
,main
和test
2个里面又有java
和resources
,刚开始我们创建这些文件夹即可。
在项目文件夹
HelloMaven
中创建一个名为pom.xml
的文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.makwan.maven</groupId>
<artifactId>hello-maven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>HelloMaven</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>在
src/main/java/com/makwan/maven
目录下新建一个HelloMaven.java
1
2
3
4
5
6
7package com.makwan.maven;
public class HelloMaven {
public void sayHello() {
System.out.println("Hello maven!");
}
}在
src/test/java/com/makwan/maven
目录下新建一个HelloMavenTest.java
1
2
3
4
5
6
7
8
9
10
11package com.makwan.maven;
import org.junit.Test;
public class HelloMavenTest {
public void testSayHello() {
HelloMaven hw = new HelloMaven();
hw.sayHello();
}
}使用命令行,进入到Java项目
HelloMaven
的根目录,那你就可以执行以下这些Maven命令mvn clean
清除输出目录,即把Maven把target
输出目录删除mvn compile
编译整个项目,并将编译后的内容置于输出目录target
中mvn test
执行项目里的所有测试mvn package
将项目打成jar
包并置于输出目录target
中mvn install
将项目打成jar
包并将其放置到个人仓库中
这些就是最常用的Maven命令,同时组合命令也是可以的,比如运行
mvn clean compile
,这样Maven就会先把target
清除掉再进行编译
pom.xml
详解
前面只是弄了一个极简的项目并让Maven来管理它,让它跑起来,但仅仅会用会复制粘贴是没什么用的,理解它内在的一些概念和原理才是最重要的。其中,充分理解pom.xml
中某些元素才能让我们日后对Maven运用自如。
pom.xml
总体概览
一个pom.xml
管理一个项目,一个pom.xml
的内容大概就是以下这些: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
36
37
38
39
40
41
42<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>
<!-- The Basics -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>...</packaging>
<dependencies>...</dependencies>
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
<modules>...</modules>
<properties>...</properties>
<!-- Build Settings -->
<build>...</build>
<reporting>...</reporting>
<!-- More Project Information -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- Environment Settings -->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles>
</project>
其中,The Basics
、Build Settings
这两块是我们需要着重学习的,其余部分的重要性
1 | <?xml version="1.0" encoding="UTF-8"?> |
什么是构件?什么是依赖?
构件(artifact)
如果去查词典,可发现这一词在英文直译中就是“人工制品”,“手工艺品”,看着完全跟我们软件开发这块毫无关系,但再结合软件编程上下文进行更加深化地理解,其实在开发过程中通过程序员所制造出来的产物都可以将它理解为“构件”(artifact),如果你通过Google查询,也会得到含以上类似的解释:
维基百科(Wikipedia):An artifact is one of many kinds of tangible by-products produced during the development of software.
翻译过来讲就是构件是在软件开发期间所产生的多种有形产物之一。
更具体地说,源代码、文档、可执行文件、测试用例等等都可以看作是构件;
而在Maven中或平日一般的Java开发中,构件一般指的是模块(比如在Spring
中,spring-context
、spring-core
、spring-aop
等等)
依赖(dependency)
在Maven中依赖指的是jar
包。
什么是Maven坐标?
Maven坐标(coordinate)概念
坐标这一概念在几何中我们是最熟悉不过了,任何一个坐标都能唯一标识平面或空间内的一个点
类似的在Maven中,任何一个坐标都能唯一标识Maven仓库中的一个依赖想想以前,如果你要找一个
jar
包,到各种网站搜索,并且还要注意版本冲突问题,是十分麻烦的。但现在有了Maven所定义的坐标这一概念,使得世界上任何一个依赖都可以使用Maven坐标唯一标识,这样我们就能通过Maven坐标来定位查询下载并管理大量的依赖,这使得开发人员能够从这些必要且琐碎的工作中解脱出来。
Maven坐标组成
在几何中,坐标由x轴
、y轴
、z轴
等由人定义的数轴上的点组成,在Maven中也不例外,Maven坐标也是由几个部分组成,它才能真真正正地对所有依赖进行唯一标识。Maven坐标由3个元素所组成,只要明确这3个元素是什么,那就能明确坐标标识哪个依赖,更简短直白来说就是Maven坐标是三位一体的:groupId
、artifactId
、version
1
2
3<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>在我们开始理解这3个元素之前,我们必须带着这种思维:一个实际项目是由一个或多个模块组成,模块可以说是一个实际项目中的子项目。
groupId
:当前模块隶属的项目
一般来说,给groupId
所指定的值都是Java中的包名,即域名反写。所以这个值的一般格式是这样的域名后缀.组织名或公司名.实际项目名
,而域名后缀以及组织名或公司名都不是必须的。比如:带组织名或公司名org.apache.commons
、不带组织名或公司名org.springframework
、只有实际项目名junit
具体例子:
如果有这么一个模块a
,它的groupId
是org.xxx.yyyy
,那么我们就可以说模块a
是隶属于xxx
非盈利组织下的一个实际项目yyyy
,模块a
实际会被下载并存放在本地仓库的org/xxx/yyyy
目录内。artifactId
:当前实际项目中的模块
我们给artifactId
所指定的值一般都遵循这2种形式:实际项目名-模块名
、实际项目名简写-模块名
,当然这也只是一种约定,并不是硬性规定,但我们最好还是遵守这种约定,这是因为在默认情况下Maven生成的构件是直接用artifactId
作为前缀并后加版本号进行命名的,如果在这前面加上实际项目名,则能够更好地用命名区分,所以在此处遵守约定是有它的道理以及好处的。
比如:在实际项目org.springframework
内有这些模块:spring-context
、spring-core
、spring-aop
等等。version
:当前模块的版本号
这个就更容易理解了,前面也说过Maven在生成构件的时候会在后面加版本号,指的就是这个,每个模块都有属于自己的版本号。版本号形式如下:1.0
、1.01
、4.53
等等。
以上3个元素是最重要也是必须定义的。有了这3个元素就能唯一标识任意一个依赖了,不过还有2个不太重要的元素也是Maven坐标组成的部分。
packaging
:当前模块的打包方式
我们可以给它指定2种值:jar
和war
。默认情况下,该元素的值为jar
,所以这个元素定义是可选的。classifier
:当前模块生成构件时所附带的一些附属构件
对一个模块生成构件时可以同时输出javadoc
、sources
等附属构件,即与该模块相关的Java文档以及源代码,它们就是附属构件。但是,这个元素并不能直接定义,因为这些附属构件不是由项目直接默认生成的,而是由附加的插件生成的。