什么是 Maven
前言
Maven 翻译为 " 专家 " 、" 内行 ",是 Apache 下的一个纯 Java 开发的开源项目。基于项目对象模型(缩写:POM)概念,Maven 利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。
Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。
Maven 也可被用于构建和管理各种项目,例如 C#、Ruby、Scala 和其他语言编写的项目。Maven 曾是 Jakarta 项目的子项目,现为由 Apache 软件基金会主持的独立 Apache 项目。
一、Maven 的概念
1.1、Maven 的用途
Maven 是干什么用的?这是很多同学在刚开始接触 Maven 时最大的问题。之所以会提出这个问题,是因为即使不使用 Maven 我们仍然可以进行 B/S 结构项目的开发。从表述层、业务逻辑层到持久化层。再到数据库都有成熟的解决方案——不使用 Maven 我们一样可以开发项目啊!
为什么要使用 Maven?它能帮助我们解决什么问题?
以更轻的方式添加第三方 jar 包
在今天的 JavaEE 开发领域,有大量的第三方框架和工具可以供我们使用。要使用这些 jar 包最简单的方法就是复制粘贴到 WEB-INF/lib 目录下。但是这会导致每次创建一个新的工程就需要将 jar 包重复复制到 lib 目录下,从而造成工作区中存在大量重复的文件,让我们的工程显得很臃肿。而使用 Maven 后每个 jar 包本身只在本地仓库中保存一份,需要 jar 包的工程只需要以坐标的方式简单的引用一下就可以了。不仅极大的节约了存储空间,让项目更轻巧,更避免了重复文件太多而造成的混乱。
自动处理 jar 包之间的依赖关系
jar 包往往不是孤立存在的,很多 jar 包都需要在其他 jar 包的支持下才能够正常工作,我们称之为 jar 包之间的依赖关系。最典型的例子是:commons-fileupload-1.3.jar 依赖于 commons-io-2.0.1.jar,如果没有 IO 包,FileUpload 包就不能正常工作。那么问题来了,你知道你所使用的所有 jar 包的依赖关系吗?当你拿到一个新的从未使用过的 jar 包,你如何得知他需要哪些 jar 包的支持呢?如果不了解这个情况,导入的 jar 包不够,那么现有的程序将不能正常工作。
再进一步,当你的项目中需要用到上百个 jar 包时,你还会人为的,手工的逐一确认它们依赖的其他 jar 包吗?这简直是不可想象的。而引入 Maven 后,Maven 就可以替我们自动的将当前 jar 包所依赖的其他所有 jar 包全部导入进来,无需人工参与,节约了我们大量的时间和精力。用实际例子来说明就是:通过 Maven 导入 commons-fileupload-1.3.jar 后,commons-io-2.0.1.jar 会被自动导入,程序员不必了解这个依赖关系。下图是 Spring 所需 jar 包的部分依赖关系:
统一的 jar 包管理体系
JavaEE 开发中需要使用到的 jar 包种类繁多,几乎每个 jar 包在其本身的官网上的获取方式都不尽相同。为了查找一个 jar 包找遍互联网,身心俱疲,没有经历过的人或许体会不到这种折磨。不仅如此,费劲心血找的 jar 包里有的时候并没有你需要的那个类,又或者又同名的类没有你要的方法——以不规范的方式获取的 jar 包也往往是不规范的。使用 Maven 我们可以享受到一个完全统一规范的 jar 包管理体系。你只需要在你的项目中以坐标的方式依赖一个 jar 包,Maven 就会自动从中央仓库进行下载,并同时下载这个 jar 包所依赖的其他 jar 包 —— 规范、完整、准确!一次性解决所有的问题!
将项目细化为多个工程模块
随着 JavaEE 项目的规模越来越庞大,开发团队的规模也与日俱增。一个项目上千人的团队持续开发很多年对于 JavaEE 项目来说再正常不过。那么我们想象一下:几百上千的人开发的项目是同一个 Web 工程。那么架构师、项目经理该如何划分项目的模块、如何分工呢?这么大的项目已经不可能通过 package 结构来划分模块,必须将项目拆分成多个工程协同开发。多个模块工程中有的是 Java 工程,有的是 Web 工程。
小提示
在这里我们顺便说一下,统一的规范几乎可以说成是程序员的最高信仰。如果没有统一的规范,就意味着每个具体的技术都各自为政,需要以诸多不同的特殊的方式加入到项目中;
好不容易加入进来还会和其他技术格格不入,最终受苦的是我们。而任何一个领域的统一规范都能够极大的降低程序员的工作难度,减少工作量。例如:USB 接口可以外接各种设备,如果每个设备都有自己独特的接口,那么不仅制造商需要维护各个接口的设计方案,使用者也需要详细了解每个设备对应的接口,无疑是非常繁琐的。
1.2、什么是 Maven
Maven 是一款服务于 Java 平台的自动化构建工具,这里面的构建指的就是以 “ Java 源文件 ”、“ 框架配置文件 ”、“ JSP ”、“ HTML ”、“ CSS ”、“ 图片 ”等资源为 “ 原材料 ”,去生产一个可运行的项目过程,而这个构建过程可以细分为以下的几个步骤:
清理:将以前编译得到的 class 字节码文件删除,为下一次编译做准备
编译:将 Java 源代码编译成 class 字节码文件
测试:自动执行测试代码,调用 Junit 程序
报告:输出测试程序的执行结果
打包:将工程文件打包为可以被服务器执行或自带服务器的包文件,Web 工程打为 war 包,Java 工程打为 jar 包
安装:这是 Maven 中的一个特定概念,指的是将打包得到的文件复制到 "仓库" 的特定位置,便于引用,如果是在本地运行的,一般会安装在指定的本地仓库中。此外,我们也可以将自己写好的工具抽象成依赖,上传到 Maven 中心仓库。
部署:将动态 Web 工程生成的 war 包复制到 Servlet 容器的指定目录下,使其可以运行
小提示
在运行时环境里面,Tomcat、JRE1.8 本身只是一组 jar 包的引用,并没有把 jar 包本身复制到工程中,所以并不是目录。此外,开发过程中,所有的路径或配置文件中配置的类路径等都是以编译结果的目录结构为标准。
像 Maven 这样的自动化构建工具还有 Ant 和 Gradle,不过在国内,使用最多的就是 Maven,而 Gradle 的话,在国外比较流行,Spring 源码的搭建似乎就是基于 Gradle 的。
二、如何安装 Maven
下载
Maven 的官网:Maven – Download Apache Maven
官网上选择对应系统的压缩包文件进行下载即可。此外,关于 Maven 的版本的选择,我建议是下载 3.6.2+ 的版本即可,太旧的版本可能不支持 JDK1.8。
系统需求
JDK:Maven 3.3+ 版本需要 JDK 1.7 及以上的版本才能执行,但是如果使用Toolchains 可以让 Maven 向下兼容 1.3 或其他版本的 JDK 项目搭建
内存:没有要求
硬盘:安装 Maven 需要大约 10 MB 的空间。除此之外,还需要有一部分空闲的空间用于本地 Maven 仓库,其大小根据使用情况而定,但预计最少也需要 500 MB,所以总的要求是 510 MB 左右。
操作系统:没有要求,只要能启动 shell 脚本或 Windows 批处理文件即可。
具体的安装步骤如下:
在安装前,我们需要先检查 Java 的环境变量,因为 Maven 是使用纯 Java 语言开发的项目,需要 Java 环境支持才能运行,在命令行中输入 java -version
,如果出现以下信息,说明没有问题
如果出现下面这段提示,说明本机的 Java 环境没有配置好或没有安装 JDK,关于如何配置 Java 环境或者如何安装 JDK 可以参考我的这篇文章:JDK 的安装和配置
'java' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
复制代码
解压压缩包,将文件夹放入指定的位置
Maven 的安装没有专门的图形化安装程序,所以在安装的时候,只需要将对应的压缩包解压至你想安装的位置即可。
配置对应的环境变量
这一步的存在是为了方便其他的软件使用 Maven ,比如 IDEA。一共需要在系统环境中配置两个环境变量:
第一个
变量名:M2_HOME
变量值:Maven的安装位置
第二个
变量名:path
变量值:Maven的安装位置\bin
配置本地仓库位置和阿里云镜像
这是安装的最后一步,本地仓库的位置可以自定义,不过笔者建议是 Maven 装在哪个盘,本地仓库就配置在哪,例如 Maven 装在 D 盘,那么 本地仓库的位置可以指定为 D:\RepMaven:
<localRepository>D:\RepMaven</localRepository>
复制代码
阿里云镜像的话,复制下面这段代码即可:
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
<mirror>
<id>repo2</id>
<name>repo2 maven</name>
<url>http://repo2.maven.org/maven2</url>
<mirrorOf>central</mirrorOf>
</mirror>
复制代码
另外,JDK 的版本也需要在 Maven 的配置文件中进行指定:
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
复制代码
把上面的配置粘贴在 settings.xml 文件中即可:
完整的 xml 配置如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!--
| This is the configuration file for Maven. It can be specified at two levels:
|
| 1. User Level. This settings.xml file provides configuration for a single user,
| and is normally provided in ${user.home}/.m2/settings.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -s /path/to/user/settings.xml
|
| 2. Global Level. This settings.xml file provides configuration for all Maven
| users on a machine (assuming they're all using the same Maven
| installation). It's normally provided in
| ${maven.conf}/settings.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -gs /path/to/global/settings.xml
|
| The sections in this sample file are intended to give you a running start at
| getting the most out of your Maven installation. Where appropriate, the default
| values (values used when the setting is not specified) are provided.
|
|-->
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<!-- Maven本地仓库配置在这里 -->
<localRepository>D:\RepMaven</localRepository>
<!-- interactiveMode
| This will determine whether maven prompts you when it needs input. If set to false,
| maven will use a sensible default value, perhaps based on some other setting, for
| the parameter in question.
|
| Default: true
<interactiveMode>true</interactiveMode>
-->
<!-- offline
| Determines whether maven should attempt to connect to the network when executing a build.
| This will have an effect on artifact downloads, artifact deployment, and others.
|
| Default: false
<offline>false</offline>
-->
<!-- pluginGroups
| This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e.
| when invoking a command line like "mvn prefix:goal". Maven will automatically add the group identifiers
| "org.apache.maven.plugins" and "org.codehaus.mojo" if these are not already contained in the list.
|-->
<pluginGroups>
<!-- pluginGroup
| Specifies a further group identifier to use for plugin lookup.
<pluginGroup>com.your.plugins</pluginGroup>
-->
</pluginGroups>
<!-- proxies
| This is a list of proxies which can be used on this machine to connect to the network.
| Unless otherwise specified (by system property or command-line switch), the first proxy
| specification in this list marked as active will be used.
|-->
<proxies>
<!-- proxy
| Specification for one proxy, to be used in connecting to the network.
|
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<username>proxyuser</username>
<password>proxypass</password>
<host>proxy.host.net</host>
<port>80</port>
<nonProxyHosts>local.net|some.host.com</nonProxyHosts>
</proxy>
-->
</proxies>
<!-- servers
| This is a list of authentication profiles, keyed by the server-id used within the system.
| Authentication profiles can be used whenever maven must make a connection to a remote server.
|-->
<servers>
<!-- server
| Specifies the authentication information to use when connecting to a particular server, identified by
| a unique name within the system (referred to by the 'id' attribute below).
|
| NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are
| used together.
|
<server>
<id>deploymentRepo</id>
<username>repouser</username>
<password>repopwd</password>
</server>
-->
<!-- Another sample, using keys to authenticate.
<server>
<id>siteServer</id>
<privateKey>/path/to/private/key</privateKey>
<passphrase>optional; leave empty if not used.</passphrase>
</server>
-->
</servers>
<!-- mirrors
| This is a list of mirrors to be used in downloading artifacts from remote repositories.
|
| It works like this: a POM may declare a repository to use in resolving certain artifacts.
| However, this repository may have problems with heavy traffic at times, so people have mirrored
| it to several places.
|
| That repository definition will have a unique id, so we can create a mirror reference for that
| repository, to be used as an alternate download site. The mirror site will be the preferred
| server for that repository.
|-->
<mirrors>
<!-- mirror
| Specifies a repository mirror site to use instead of a given repository. The repository that
| this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
|
<mirror>
<id>mirrorId</id>
<mirrorOf>repositoryId</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://my.repository.com/repo/path</url>
</mirror>
-->
<!-- 阿里云镜像配置在这里 -->
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
<mirror>
<id>repo2</id>
<name>repo2 maven</name>
<url>http://repo2.maven.org/maven2</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
<!-- profiles
| This is a list of profiles which can be activated in a variety of ways, and which can modify
| the build process. Profiles provided in the settings.xml are intended to provide local machine-
| specific paths and repository locations which allow the build to work in the local environment.
|
| For example, if you have an integration testing plugin - like cactus - that needs to know where
| your Tomcat instance is installed, you can provide a variable here such that the variable is
| dereferenced during the build process to configure the cactus plugin.
|
| As noted above, profiles can be activated in a variety of ways. One way - the activeProfiles
| section of this document (settings.xml) - will be discussed later. Another way essentially
| relies on the detection of a system property, either matching a particular value for the property,
| or merely testing its existence. Profiles can also be activated by JDK version prefix, where a
| value of '1.4' might activate a profile when the build is executed on a JDK version of '1.4.2_07'.
| Finally, the list of active profiles can be specified directly from the command line.
|
| NOTE: For profiles defined in the settings.xml, you are restricted to specifying only artifact
| repositories, plugin repositories, and free-form properties to be used as configuration
| variables for plugins in the POM.
|
|-->
<profiles>
<!-- profile
| Specifies a set of introductions to the build process, to be activated using one or more of the
| mechanisms described above. For inheritance purposes, and to activate profiles via <activatedProfiles/>
| or the command line, profiles have to have an ID that is unique.
|
| An encouraged best practice for profile identification is to use a consistent naming convention
| for profiles, such as 'env-dev', 'env-test', 'env-production', 'user-jdcasey', 'user-brett', etc.
| This will make it more intuitive to understand what the set of introduced profiles is attempting
| to accomplish, particularly when you only have a list of profile id's for debug.
|
| This profile example uses the JDK version to trigger activation, and provides a JDK-specific repo.
<profile>
<id>jdk-1.4</id>
<activation>
<jdk>1.4</jdk>
</activation>
<repositories>
<repository>
<id>jdk14</id>
<name>Repository for JDK 1.4 builds</name>
<url>http://www.myhost.com/maven/jdk14</url>
<layout>default</layout>
<snapshotPolicy>always</snapshotPolicy>
</repository>
</repositories>
</profile>
-->
<!--
| Here is another profile, activated by the system property 'target-env' with a value of 'dev',
| which provides a specific path to the Tomcat instance. To use this, your plugin configuration
| might hypothetically look like:
|
| ...
| <plugin>
| <groupId>org.myco.myplugins</groupId>
| <artifactId>myplugin</artifactId>
|
| <configuration>
| <tomcatLocation>${tomcatPath}</tomcatLocation>
| </configuration>
| </plugin>
| ...
|
| NOTE: If you just wanted to inject this configuration whenever someone set 'target-env' to
| anything, you could just leave off the <value/> inside the activation-property.
|
<profile>
<id>env-dev</id>
<activation>
<property>
<name>target-env</name>
<value>dev</value>
</property>
</activation>
<properties>
<tomcatPath>/path/to/tomcat/instance</tomcatPath>
</properties>
</profile>
-->
<!-- JDK版本配置在这里 -->
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
<!-- activeProfiles
| List of profiles that are active for all builds.
|
<activeProfiles>
<activeProfile>alwaysActiveProfile</activeProfile>
<activeProfile>anotherAlwaysActiveProfile</activeProfile>
</activeProfiles>
-->
</settings>
复制代码
三、关于 Maven 的其他知识
Maven 的核心概念
Maven 的常用命令
Maven 的依赖
3.1、Maven 的核心概念
约定
POM
坐标
依赖
仓库
生命周期/插件/目标
继承
聚合
冷知识
SNAPSHOT:快照,表示不稳定的瞬间版本工程
RELEASE:分离,表示开发完成的正式版本工程
3.2、Maven 的常用命令
前言
执行与构建过程相关的 Maven 命令,必须进入 pom.xml 所在的目录下
常用命令:
mvn clean
:清理旧的 class 字节码文件
mvn compile
:编译主程序
mvn test-compile
:编译测试程序
mvn test
:执行测试
mvn package
:打包
mvn install
:安装
mvn site
:生成站点
冷知识
POM 全称 Project Object Model,意为项目对象模型。pom 文件对于 Maven 工程来说是核心配置文件,与构建过程相关的一切设置都在这个文件中进行配置,其重要程度堪比 Web 工程中的 web.xml 文件。
一般 pom 文件中会使用下面三个向量在仓库中定位唯一一个 Maven 工程
groupId:一般为公司或组织域名倒序 + 项目名称
<groupId>com.bbc.maven</groupId>
artifactId:项目中某个具体模块的名称
<artifactId>HelloWorld</artifactId>
version:依赖的具体版本号
<version>3.8.2</version>
1. 本地仓库:当前电脑上部署的仓库目录,为当前电脑所有 Maven 工程服务
2. 远程仓库
1. 私服:架设在当前局域网环境下的,为当前局域网范围内的所有 Maven 服务
2. 中央仓库:架设在 Internet 上,为全世界的 Maven 项目服务
3. 中央仓库镜像:架设在各大洲,为中央仓库分担流量,减轻中央仓库的压力,同时更快地响应用户的请求
1. Maven 自身所需插件
2. 第三方框架或工具的 jar 包
3. 个人开发的 Maven 工程 (通常在本地 Maven 仓库中)
3.3、Maven 的依赖
Maven 在解析 pom 文件的依赖信息时首先会在本地仓库查找被依赖的 jar 包文件,如果查找不到,才会自动连接外网到中央仓库中进行下载,如果此时无法下载,则搭建失败。此外,如果我们开发的 Maven 工程执行过 install 命令,那么也会进入本地仓库。
依赖的作用范围
依赖的传递性
依赖的传递性可以使得所有工程中不必都声明一次依赖,在父 pom 文件中声明一次即可。注意,非 compile 范围的依赖无法传递,所以必须在需要声明的地方重复声明依赖
如何排除某个依赖项中的其他依赖?
使用 `exclusions` 和 `exclusion` 标签排除某个依赖中的子依赖,如下所示:
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
复制代码
怎么统一更改依赖的版本?
引入 `properties` 标签,在需要同一版本的位置,使用 `${自定义版本标签名}` 引用声明的版本号,如下所示:
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<version>${maven.compiler.source}</version>
复制代码
小提示
第三方 jar 包的依赖信息可以在 Maven 酷站进行搜索:http://mvnrepository.com/
评论