Maven基础知识(2)- Maven 坐标、Maven 外部依赖、Maven 仓库

 

1. Maven 坐标

    在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称为构件(组件)。在 Maven 世界中存在着数十万甚至数百万构件,在引入坐标概念之前,当用户需要使用某个构件时,只能去对应的网站寻找,但各个网站的风格迥异,这使得用户将大量的时间浪费在搜索和寻找上,严重地影响了研发效率。为了解决这个问题,于是 Maven 引入了 Maven 坐标的概念。

    Maven 坐标一套规则,它规定:世界上任何一个构件都可以使用 Maven 坐标并作为其唯一标识,Maven 坐标包括 groupId、artifactId、version、packaging 等元素,只要用户提供了正确的坐标元素,Maven 就能找到对应的构件。

    任何一个构件都必须明确定义自己的坐标,这是 Maven 的强制要求,任何构件都不能例外。我们在开发 Maven 项目时,也需要为其定义合适的坐标,只有定义了坐标,其他项目才能引用该项目生成的构件。

    以下是 MavenDemo01 项目的坐标定义。

1         <project>
2             <groupId>com.example</groupId>
3             <artifactId>MavenDemo01</artifactId>
4             <packaging>jar</packaging>
5             <version>1.0-SNAPSHOT</version>
6         </project>


    Maven 坐标主要由以下元素组成:

        groupId: 项目组 ID,定义当前 Maven 项目隶属的组织或公司,通常是唯一的。它的取值一般是项目所属公司或组织的网址或 URL 的反写,例如 com.example;
        artifactId: 项目 ID,通常是项目的名称;
        version:版本;
        packaging:项目的打包方式,默认值为 jar;

    以上 4 个元素中 groupId、artifactId 和 version 是必须定义的,packaging 是可选的。


2. Maven 外部依赖

    Maven 是一款优秀的依赖管理工具,那么什么是外部依赖呢?

    通俗的说,如果一个 Maven 构建所产生的构件(例如 Jar 文件)被其他项目引用,那么该构件就是其他项目的依赖。

    1) 依赖声明

        Maven 坐标是依赖的前提,所有 Maven 项目必须明确定义自己的坐标,只有这样,它们才可能成为其他项目的依赖。当一个项目的构件成为其他项目的依赖时,该项目的坐标才能体现出它的价值。

        当 Maven 项目需要声明某一个依赖时,通常只需要在其 POM 中配置该依赖的坐标信息,Maven 会根据坐标自动将依赖下载到项目中。

        例如,某个项目中使用 servlet-api 作为其依赖,其配置如下。

 1             <project xmlns="http://maven.apache.org/POM/4.0.0"
 2                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
 4                                         http://maven.apache.org/maven-v4_0_0.xsd">
 5                 
 6                 ...
 7                 
 8                 <dependencies>
 9                     <dependency>
10                         <groupId>javax.servlet</groupId>
11                         <artifactId>servlet-api</artifactId>
12                         <version>2.5</version>
13                         <scope>provided</scope>
14                     </dependency>
15                 </dependencies>
16             </project>


        dependencies 元素可以包含一个或者多个 dependency 子元素,用以声明一个或者多个项目依赖,每个依赖都可以包含以下元素:

            (1) groupId、artifactId 和 version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven 根据坐标才能找到需要的依赖;
            (2) type:依赖的类型,对应于项目坐标定义的 packaging。大部分情况下,该元素不必声明,其默认值是 jar;
            (3) scope:依赖的范围;
            (4) optional:标记依赖是否可选;
            (5) exclusions:用来排除传递性依赖;

        大部分依赖声明只包含 groupId、artifactId 和 version 三个元素,至于 scope、optional 以及 exclusions 等元素,了解即可,在后续的学习中我们会陆续进行讲解。

    2) 获取依赖坐标

        通常情况下,绝大部分依赖的 Maven 坐标都能在 https://mvnrepository.com/ 中获取。

        例如,当项目中需要引入 servlet-api 时, 只需要在首页搜索 servlet-api 即可。

        选择合适的版本,在依赖详情页的最下方就是该版本依赖的 Maven 坐标,我们可以直接将其复制到项目的 pom.xml 中使用。

    3) 导入本地 JAR 包

        Maven 是通过仓库对依赖进行管理的,当 Maven 项目需要某个依赖时,只要其 POM 中声明了依赖的坐标信息,Maven 就会自动从仓库中去下载该构件使用。
        
        在实际的开发过程中,经常会遇到一种情况:某一个项目需要依赖于存储在本地的某个 jar 包,该 jar 包无法从任何仓库中下载的,这种依赖被称为外部依赖或本地依赖。
        
        下面通过一个实例来介绍如何导入本地 jar 包,示例:

        (1) 创建 MavenDemo02 项目

            打开 cmd 命令行窗口,进入 D:\Workshop\maven 目录,

            D:\Workshop\maven>​mvn archetype:generate -DgroupId=com.example -DartifactId=MavenDemo02 -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

        (2) 修改 MavenDemo02 中 App 类的代码如下

1             package com.example;
2             
3             import com.example.Common;
4 
5             public class App {
6                 public static void main(String[] args) {
7                     Common.sayHello("MavenDemo02: say hello");
8                 }
9             }


            MavenDemo02 中的 App 类需要使用 MavenDemo01 中的 Common 类,即 MavenDemo02 需要依赖于 MavenDemo01,需要导入 MavenDemo01-1.0-SNAPSHOT.jar。
            
            MavenDemo01-1.0-SNAPSHOT.jar 的打包过程,请参考 “Maven基础知识(1)- Maven 简介、Maven 安装配置、创建 Quickstart 项目”。

        (3) 修改 MavenDemo02 中 pom.xml 的配置如下

 1             <project xmlns="http://maven.apache.org/POM/4.0.0" 
 2                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
 4                                         http://maven.apache.org/maven-v4_0_0.xsd">
 5 
 6                 <modelVersion>4.0.0</modelVersion>
 7                 <groupId>com.example</groupId>
 8                 <artifactId>MavenDemo02</artifactId>
 9                 <packaging>jar</packaging>
10                 <version>1.0-SNAPSHOT</version>
11                 <name>MavenDemo02</name>
12                 <url>http://maven.apache.org</url>
13                 <dependencies>
14                     <dependency>
15                         <groupId>junit</groupId>
16                         <artifactId>junit</artifactId>
17                         <version>3.8.1</version>
18                         <scope>test</scope>
19                     </dependency>
20 
21                     <!--外部依赖-->
22                     <dependency>
23                         <groupId>com.example</groupId>
24                         <artifactId>MavenDemo01</artifactId>
25                         <version>1.0-SNAPSHOT</version>
26                         <!-- 依赖范围 -->
27                         <scope>system</scope>
28                         <!-- 依赖所在位置 -->
29                         <systemPath>D:\Workshop\maven\MavenDemo01\target\MavenDemo01-1.0-SNAPSHOT.jar</systemPath>
30                     </dependency>
31                 </dependencies>
32             </project>

 

            在以上配置中,除了依赖的坐标信息外,外部依赖还使用了 scope 和 systemPath 两个元素。

                (1) scope 表示依赖范围,这里取值必须是 system,即系统。
                (2) systemPath 表示依赖的本地构件的位置。

        (4) 编译运行
        
            执行如下命令:

                D:\Workshop\maven\MavenDemo02>mvn clean compile

            在项目根目录中生成了一个名为 target 的目录,该目录包含以下文件:

                target
                    |- classes
                    |- maven-status

 

            执行如下命令:

                D:\Workshop\maven\MavenDemo02>cd target\classes

                D:\Workshop\maven\MavenDemo02\target\classes>java com.example.App

            显示结果如下:

Exception in thread "main" java.lang.NoClassDefFoundError: com/example/Common
at com.example.App.main(App.java:7)
Caused by: java.lang.ClassNotFoundException: com.example.Common
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
...

注:错误信息显示,没有发现 com.example.Common 类,也就是在运行环境里没有找到 MavenDemo01-1.0-SNAPSHOT.jar 包。

   

    设置本地临时环境变量 classpath,再运行:

D:\Workshop\maven\MavenDemo02\target\classes>set classpath=%classpath%;D:\Workshop\maven\MavenDemo01\target\MavenDemo01-1.0-SNAPSHOT.jar
D:\Workshop\maven\MavenDemo02\target\classes>java com.example.App

    MavenDemo02: say hello

 

注:导入本地 JAR 包的方式,编译时需要指定 <systemPath> 固定全路径,运行时需要把依赖包设置到本地环境变量 classpath。很显然这种硬编码路径的方式,在编译和运行过程中,为了找到 MavenDemo01-1.0-SNAPSHOT.jar 包,都需要做额外的配置,不是一个好的解决方案。

Maven 提供的解决方案是安装 JAR 包到本地 Maven 仓库。

    4) 安装 JAR 包到本地 Maven 仓库

Maven 提供了 mvn install 命令来实现安装 JAR 包到本地 Maven 仓库,假设本地 Maven 仓库的路径是 C:\Applications\java\maven-repository 。

打开 cmd 命令行窗口,进入 D:\Workshop\maven\MavenDemo01\ 目录,运行如下命令:

D:\Workshop\maven\MavenDemo01> mvn install

在目录 C:\Applications\Java\maven-repository\com\example 里就可以看到被安装的 MavenDemo01 包。

修改上文 MavenDemo02 中 pom.xml 的配置如下:

 1             <project xmlns="http://maven.apache.org/POM/4.0.0" 
 2                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
 4                                         http://maven.apache.org/maven-v4_0_0.xsd">
 5                     ...
 6 
 7                     <!--外部依赖-->
 8                     <dependency>
 9                         <groupId>com.example</groupId>
10                         <artifactId>MavenDemo01</artifactId>
11                         <version>1.0-SNAPSHOT</version>
12                     </dependency>
13                 </dependencies>
14             </project>

 

执行编译命令:

  D:\Workshop\maven\MavenDemo02>mvn clean compile

运行如下命令:

  D:\Workshop\maven\MavenDemo02\target\classes>java com.example.App

    MavenDemo02: say hello

 

3. Maven 仓库

    在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称为构件。

    Maven 在某个统一的位置存储所有项目的构件,这个统一的位置,我们就称之为仓库。换言之,仓库就是存放依赖和插件的地方。

    任何的构件都有唯一的坐标,该坐标定义了构件在仓库中的唯一存储路径。当 Maven 项目需要某些构件时,只要其 POM 文件中声明了这些构件的坐标,Maven 就会根据这些坐标找自动到仓库中找到并使用它们。

    项目构建完成生成的构件,也可以安装或者部署到仓库中,供其他项目使用。

    Maven 仓库可以分为本地仓库和远程仓库,远程仓库还可以分为中央仓库、私服、其他公共仓库。

   1) 本地仓库

        Maven 本地仓库实际上就是本地计算机上的一个目录(文件夹),它会在第一次执行 Maven 命令时被创建。

        Maven 本地仓库可以储存本地所有项目所需的构件。当 Maven 项目第一次进行构建时,会自动从远程仓库搜索依赖项,并将其下载到本地仓库中。当项目再进行构建时,会直接从本地仓库搜索依赖项并引用,而不会再次向远程仓库获取。

        Maven 本地仓库默认地址为 C:\%USER_HOME%\.m2\repository ,但出于某些原因(例如 C 盘空间不够),我们通常会重新自定义本地仓库的位置。这时需要修改 %MAVEN_HOME%\conf 目录下的 settings.xml 文件,通过 localRepository 元素定义另一个本地仓库地址,例如:

1             <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
2                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3                     xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
4                                         http://maven.apache.org/xsd/settings-1.0.0.xsd">
5             
6                 <localRepository>D:/java/maven-repository</localRepository>
7             
8             </settings>

        构件只有储存在本地仓库中,才能被其他的 Maven 项目使用。构件想要进入本地仓库,除了从远程仓库下载到本地仓库外,还可以使用命令 mvn install 将本地项目的输出构件安装到本地仓库中。

    2) 中央仓库

        中央仓库是由 Maven 社区提供的一种特殊的远程仓库,它包含了绝大多数流行的开源构件。在默认情况下,当本地仓库没有 Maven 所需的构件时,会首先尝试从中央仓库下载。

        中央仓库具有如下特点:

            (1) 由 Maven 社区管理
            (2) 不需要配置
            (3) 需要通过网络才能访问

        我们可以通过 Maven 社区提供的 URL:http://search.maven.org/#browse,浏览其中的构件。中央仓库包含了绝大多数流行的开源 Java 构件及其源码、作者信息和许可证信息等。一般来说,Maven 项目所依赖的构件都可以从中央仓库下载到。

        虽然中央仓库属于远程仓库的范畴,但由于它的特殊性,一般会把它与其他远程仓库区分开。我们常说的远程仓库,一般不包括中央仓库。

    3) 远程仓库

        如果 Maven 在本地仓库和中央仓库中都找不到依赖的库文件,它就会停止构建过程并输出错误信息到控制台。为避免这种情况的发生,Maven 还提供了远程仓库的概念,它是一种由开发人员自己定制的仓库,其中包含了供其他项目使用的代码库或者构件。

 1         <project xmlns="http://maven.apache.org/POM/4.0.0"
 2                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
 4                                     https://maven.apache.org/xsd/maven-4.0.0.xsd">
 5 
 6             <modelVersion>4.0.0</modelVersion>
 7             <groupId>com.example</groupId>
 8             <artifactId>MavenDemo01</artifactId>
 9             <version>0.0.1-SNAPSHOT</version>
10             <dependencies>
11                 <dependency>
12                     <groupId>com.example.common-lib</groupId>
13                     <artifactId>common-lib</artifactId>
14                     <version>1.0.0</version>
15                 </dependency>
16             </dependencies>
17             <repositories>
18                 <repository>
19                     <id>test.lib1</id>
20                     <url>http://download.test.org/maven2/lib1</url>
21                 </repository>
22                 <repository>
23                     <id>test.lib2</id>
24                     <url>http://download.test.org/maven2/lib2</url>
25                 </repository>
26             </repositories>
27         </project>

    4) 依赖搜索顺序   

        (1) 从本地仓库查找构件,如果没有找到,跳到第 2 步,否则继续执行其他处理;
        (2) 从中央仓库查找构件,如果没有找到,并且已经设置其他远程仓库,然后移动到第 4 步;如果找到,那么将构件下载到本地仓库中使用;
        (3) 如果没有设置其他远程仓库,Maven 则会停止处理并抛出错误;
        (4) 在远程仓库查找构件,如果找到,则会下载到本地仓库并使用,否则 Maven 停止处理并抛出错误。

    5) Maven 仓库镜像和私服

        如果一个仓库 A 可以提供另一个仓库 B 的所有内容,那么就可以认为仓库 A 是仓库 B 的一个镜像(Mirror)。

        (1) 使用镜像代替中央仓库

            国内开发人员由于网络原因,直接从中央仓库下载构件时,速度较慢或不稳定,通常会使用中央仓库的国内镜像站来解决该问题。

            配置 Maven 镜像的方法也非常的简单,只需要在 Maven 安装 config 目录中 setting.xml 文件的 mirrors 节点中,使用 mirror 标签添加镜像的相关信息即可。

            目前国内使用最多,最稳定的中央仓库镜像分别是由阿里云和华为云提供的,它们的地址配置如下。

            阿里云镜像地址    

1             <mirror>
2                 <id>aliyun</id>
3                 <mirrorOf>central</mirrorOf>
4                 <name>aliyun</name>
5                 <url>https://maven.aliyun.com/repository/public</url>
6             </mirror>


            华为云镜像地址

1             <mirror>
2                 <id>huaweicloud</id>
3                 <name>mirror from maven huaweicloud</name>
4                 <mirrorOf>central</mirrorOf>
5                 <url>https://repo.huaweicloud.com/repository/maven/</url>
6             </mirror>


            以上配置中,mirrorOf 的取值为 central,表示该配置为中央仓库的镜像,所有对于中央仓库的请求都会转到该镜像。当然,我们也可以使用以上方式配置其他仓库的镜像。另外三个元素 id、name 和 url 分别表示镜像的唯一标识、名称和地址。

        (2) 镜像与 Maven 私服配合使用

            镜像通常会和 Maven 私服配合使用,由于 Maven 私服可以代理所有外部的公共仓库(包括中央仓库),因此对于组织内部的用户来说,使用一个私服就相当于使用了所有需要的外部仓库,这样就可以将配置集中到私服中,简化 Maven 本身的配置。这种情况下,用户所有所需的构件都可以从私服中获取,此时私服就是所有仓库的镜像。

 1             <settings>
 2                 ...
 3                 <mirrors>
 4                     <mirror>
 5                         <id>nexus</id>
 6                         <mirrorOf>*</mirrorOf>
 7                         <name>nexus</name>
 8                         <url>http://localhost:8082/nexus/content/groups/bianchengbang_repository_group/</url>
 9                     </mirror>
10                 </mirrors>
11                 ...
12             </settings>


            以上配置中,mirrorOf 元素的取值为 * ,表示匹配所有远程仓库,所有对于远程仓库的请求都会被拦截,并跳转到 url 元素指定的地址。

            为了满足一些较为复杂的需求,Maven 还支持一些更为高级的配置。

                (1) <mirrorOf>*</mirrorOf>:匹配所有远程仓库。
                (2) <mirrorOf>external:*</mirrorOf>:匹配所有远程仓库,使用 localhost 和 file:// 协议的除外。即,匹配所有不在本机上的远程仓库。
                (3) <mirrorOf>repo1,repo2</mirrorOf>:匹配仓库 repo1 和 repo2,使用逗号分隔多个远程仓库。
                (4) <mirrorOf>*,!repo1</miiroOf>:匹配所有远程仓库,repo1 除外,使用感叹号将仓库从匹配中排除。

            需要注意的是,由于镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或者停止服务时,Maven 也无法访问被镜像仓库,因而将无法下载构件。

        (3)Maven 私服

            Maven 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(中央仓库、其他远程公共仓库)。

            建立了 Maven 私服后,当局域网内的用户需要某个构件时,会按照如下顺序进行请求和下载。
        
                (1) 请求本地仓库,若本地仓库不存在所需构件,则跳转到第 2 步;
                (2) 请求 Maven 私服,将所需构件下载到本地仓库,若私服中不存在所需构件,则跳转到第 3 步。
                (3) 请求外部的远程仓库,将所需构件下载并缓存到 Maven 私服,若外部远程仓库不存在所需构件,则 Maven 直接报错。

            此外,一些无法从外部仓库下载到的构件,也能从本地上传到私服供其他人使用。

            Maven 私服具有以下 5 点优势:

                (1) 节省外网带宽: 大量对于外部远程仓库的重复请求,会消耗很大量的带宽,利用 Maven 私服代理外部仓库后,能够消除对外部仓库的大量重复请求,降低外网带宽压力。
                (2) 下载速度更快: Maven 私服位于局域网内,从私服下载构建更快更稳定。
                (3) 便于部署第三方构件: 有些构件是无法从任何一个远程仓库中获得的(例如,某公司或组织内部的私有构件、Oracle 的 JDBC 驱动等),建立私服之后,就可以将这些构件部署到私服中,供内部 Maven 项目使用。
                (4) 提高项目的稳定性,增强对项目的控制: 建立私服后,即使外部网络状况不佳甚至中断,只要私服中已经缓存了所需的构件,Maven 也能够正常运行。此外,一些私服软件(如 Nexus)还提供了很多额外控制功能,例如,权限管理、RELEASE/SNAPSHOT 版本控制等,可以对仓库进行一些更加高级的控制。
                (5) 降低中央仓库得负荷压力: 由于私服会缓存中央仓库得构件,避免了很多对中央仓库的重复下载,降低了中央仓库的负荷。

            搭建 Maven 私服的仓库管理器(Repository Manager),主要有以下 3 种:

                (1) Apache Archiva
                (2) JFrog Artifactory
                (3) Sonatype Nexus

            其中,Sonatype Nexus 是当前最流行,使用最广泛的 Maven 仓库管理器。


posted @ 2022-06-05 16:13  垄山小站  阅读(462)  评论(0编辑  收藏  举报