01.maven仓库、坐标和依赖详解

302 阅读25分钟
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- 配置maven编译的时候采用的编译器版本 -->
    <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    <!-- 指定源代码是什么版本的,如果源码和这个版本不符将报错,maven中执行编译的时候会用到这个配置,默认是1.5,这个相当于javac命令后面的-source参数 -->
    <maven.compiler.source>1.8</maven.compiler.source>
    <!-- 该命令用于指定生成的class文件将保证和哪个版本的虚拟机进行兼容,maven中执行编译的时候会用到这个配置,默认是1.5,这个相当于javac命令后面的-target参数 -->
    <maven.compiler.target>1.8</maven.compiler.target>
    <!-- 指定资源文件复制过程中采用的编码方式 -->
    <encoding>UTF-8</encoding>
    <!-- 项目的主源码目录,默认为src/main/java/ -->
    <a>${pom.build.sourceDirectory}</a>
    <!-- 项目的测试源码目录,默认为src/test/java/ -->
    <b>${project.build.testSourceDirectory}</b>
    <!-- 项目构建输出目录,默认为target/ -->
    <c>${project.build.directory}</c>
    <!-- 项目主代码编译输出目录,默认为target/classes -->
    <d>${project.build.outputDirectory}</d>
    <!-- 项目测试代码编译输出目录,默认为target/test-classes -->
    <e>${project.build.testOutputDirectory}</e>
    <!-- 项目的groupId -->
    <f>${project.groupId}</f>
    <!-- 项目的artifactId -->
    <g>${project.artifactId}</g>
    <!-- 项目的version,与${version}等价-->
    <h>${project.version}</h>
    <!-- 项目打包输出文件的名称,默认为${project.artifactId}-${project.version} -->
    <i>${project.build.finalName}</i>
    <!-- setting属性 -->
    <!-- 获取本地仓库地址-->
    <a1>${settings.localRepository}</a1>
    <!-- 系统属性 -->
    <!-- 用户目录 -->
    <a2>${user.home}</a2>
    <!-- 环境变量属性,获取环境变量JAVA_HOME的值 -->
    <a3>${env.JAVA_HOME}</a3>
</properties>

通过mvn help:effective-pom查看最终结果如下:

  <properties>
    <a>E:\WorkspaceIdea\01src\matio-maven\b2b\b2b-account\b2b-account-service\src\main\java</a>
    <a1>${settings.localRepository}</a1>
    <a2>C:\Users\Administrator</a2>
    <a3>C:\Program Files\Java\jdk1.8.0_271</a3>
    <b>E:\WorkspaceIdea\01src\matio-maven\b2b\b2b-account\b2b-account-service\src\test\java</b>
    <c>E:\WorkspaceIdea\01src\matio-maven\b2b\b2b-account\b2b-account-service\target</c>
    <d>E:\WorkspaceIdea\01src\matio-maven\b2b\b2b-account\b2b-account-service\target\classes</d>
    <e>E:\WorkspaceIdea\01src\matio-maven\b2b\b2b-account\b2b-account-service\target\test-classes</e>
    <encoding>UTF-8</encoding>
    <f>com.javacode2018</f>
    <g>b2b-account-service</g>
    <h>1.0-SNAPSHOT</h>
    <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

apache-maven-3.6.2

maven是apache软件基金会组织维护的一款自动化构件工具,专注服务于java平台的项目构件和依赖管理。

maven给每个jar定义了唯一的标志,这个在maven中叫做项目的坐标,通过这个坐标可以找到你需要用到的任何版本的jar包

maven会自动解决jar依赖的问题,比如你用到了a-1.0.jar,而a-1.0.jar依赖于b-1.1.jarc-1.5.jar,当我们通过maven把a-1.0.jar引入之后,b-1.1.jarc-1.5.jar会自动被引入进来。

maven可以很容易的解决不同版本之间的jar冲突的问题。

maven使开发者更加方便的控制整个项目的生命周期,比如:

  1. mvn clear 可以清理上次已编译好的代码
  2. mvn compile 可以自动编译项目
  3. mvn test 可以自动运行所有测试用例
  4. mvn package 可以完成打包需要的所有操作(自动包含了清理、编译、测试的过程)

maven目录

image.png

  • bin目录: 该目录包含了mvn运行的脚本,这些脚本用来配置java命令,准备好classpath和相关的Java系统属性,然后执行Java命令。

  • boot目录:  该目录只包含一个文件,该文件为plexus-classworlds-2.5.2.jar。plexus-classworlds是一个类加载器框架,相对于默认的java类加载器,它提供了更加丰富的语法以方便配置,Maven使用该框架加载自己的类库。

  • conf目录: 该目录包含了一个非常重要的文件settings.xml。直接修改该文件,就能在机器上全局地定制Maven的行为,一般情况下,我们更偏向于复制该文件至~/.m2/目录下(~表示用户目录),然后修改该文件,在用户范围定制Maven的行为。

  • lib目录: 该目录包含了所有Maven运行时需要的Java类库,Maven本身是分模块开发的,因此用户能看到诸如maven-core-3.0.jar、maven-model-3.0.jar之类的文件,此外这里还包含一些Maven用到的第三方依赖如commons-cli-1.2.jar、commons-lang-2.6.jar等等。

maven常用命令说明

    mvn clean package -U -Dmaven.test.skip=true -Pdev
    mvn site            自动生成站点信息。执行完成后,查看一下工程目录下的 target 目录,里面自动添加了一个 site 目录,都是站点信息页面。打开其中的 index.html,里面描述的就是项目相关的信息
    mvn javadoc:javadoc 自动生成 API Doc 文档。在工程中自动产生 target\site\apidocs 目录,里面就是当前工程中代码的 API Doc 文档。打开 index.html
    版本管理是指对项目的整体版本的演变过程进行管理,例如,从 1.01.1,再到 2.0 等。
    版本控制是借助第三方的版本控制工具,追踪管理项目文档(包括代码)的每一个变更

maven项目的生命周期:清理、初始化、编译、测试、打包、集成测试、验证、部署和生成站点
Maven 拥有三套独立的生命周期,它们分别是 clean、default(或build) 和 site。clean 生命周期的目的是清理项目;default 生命周期的目的是构建项目;site 生命周期的目的是建立项目站点。
每个生命周期又包含了多个阶段。这些阶段在执行的时候是有固定顺序的。后面的阶段一定要等前面的阶段执行完成后才能被执行
    mvn clean           清理上一次构建过程中生成的文件,比如编译后的 class 文件等,删除target
    mvn compile         编译项目中的源代码
    mvn test            运行测试案例
    mvn package         将编译后的 class 和资源打包成压缩文件,比如 rar
    mvn install         将打包的组件以构件的形式,安装到本地依赖仓库中,以便共享给本地的其他项目
    mvn deploy          行集成和发布环境,将测试后的最终包以构件的方式发布到远程仓库中,方便所有程序员共享
    mvn process-resources
    mvn clean install -U

mvn help:system这个命令的运行过程:

  1. 运行mvn help:system之后
  2. 系统会去环境变量PATH对应的所有目录中寻找mvn命令,然后在D:\installsoft\maven\apache-maven-3.6.2\bin中找到了可执行的mvn文件
  3. 运行mvn文件,也就是执行mvn命令
  4. 通常一些软件启动的时候,会有一个启动配置文件,maven也有,mvn命令启动的时候会去~/.m2目录寻找配置文件settings.xml,这个文件是mvn命令启动配置文件,可以对maven进行一些启动设置(如本地插件缓存放在什么位置等等),若~/.m2目录中找不到settings.xml文件,那么会去M2_HOME/conf目录找这个配置文件,然后运行maven程序
  5. mvn命令后面跟了一个参数:help:sytem,这个是什么意思呢?这个表示运行help插件,然后给help插件发送system命令
  6. maven查看本地缓存目录(默认为~/.m2目录)寻找是否有help插件,如果本地没有继续下面的步骤
  7. maven会去默认的一个站点(apache为maven提供的一个网站[repo.maven.apache.org],这个叫中央仓库)下载help插件到~/.m2目录
  8. 运行help插件,然后给help插件发送system指令,help插件收到system指令之后,输出了本地环境变量的信息,如果系统找不到指定的插件或者给插件发送无法识别的命令,都会报错

maven中所有的命令都是以插件的形式提供的,所以maven扩展也是相当容易的

maven的约定配置

Maven 提倡使用一个共同的标准目录结构,Maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构,如下所示:

目录目的
${basedir}存放pom.xml和所有的子目录
${basedir}/src/main/java项目的java源代码
${basedir}/src/main/resources项目的资源,比如说property文件,springmvc.xml
${basedir}/src/test/java项目的测试类,比如说Junit代码
${basedir}/src/test/resources测试用的资源
${basedir}/src/main/webapp/WEB-INFweb应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面
${basedir}/target打包输出目录
${basedir}/target/classes编译输出目录
${basedir}/target/test-classes测试编译输出目录
Test.javaMaven只会自动运行符合该命名规则的测试类
~/.m2/repositoryMaven默认的本地仓库目录位置

config/settings.xml

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>C:/Users/Administrator/.m2/repository</localRepository>
</settings>

1. localRepository

存储jar包的本地仓库路径,默认是${user.home}/.m2/repository

<localRepository>C:/Users/Administrator/.m2/repository</localRepository>

settings.xml中有个localRepository标签,可以设置本地缓存目录,maven从远程仓库下载下来的插件以及以后所有我们用到的jar包都会放在这个目录中,

2. proxies 代理

使用通过安全认证的代理访问外网。这种情况下,就需要为Maven配置HTTP代理,才能让它正常访问外部仓库,以下载所需要的资源

proxies下可以有多个proxy元素,如果声明了多个proxy元素,则默认情况下第一个active=true的proxy会生效

<proxies>
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <host>proxy.host.net</host>
      <port>80</port>
      <username>proxyuser</username>
      <password>proxypass</password>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
</proxies>
  • id:无特殊含义
  • active:是否激活该代理,如果为false则该proxy不生效
  • protocol:使用的代理协议,一般是http
  • host、port:目标主机和端口
  • username、password:当代理服务需要认证时,就需要配置
  • nonProxyHosts:用来指定哪些主机不需要代理,可以使用|符号来分隔多个主机名。此外,该配置也支持通配符,如:*.baidu.com表示所有以baidu.com结尾的域名访问都不要通过代理

3. servers

大部分的远程仓库不需要认证,但是如果是自己内部使用,为了安全起见,还是要配置认证信息的。 配置认证信息和配置远程仓库不同,远程仓库可以直接在pom.xml中配置,但是认证信息必须配置在settings.xml文件中。这是因为pom往往是被提交到代码仓库中供所有成员访问的,而settings.xml一般只存在于本机。因此,在settings.xml中配置认证信息更为安全。

<servers>
    <server>
      <id>deploymentRepo</id>
      <username>name</username>
      <password>pwd</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>

id要跟你在pom.xml里面配置的远程仓库repository的id一致,正是这个id将认证信息与仓库配置关联在一起。

4. mirrors 镜像

如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。用过Maven的都知道,国外的中央仓库用起来太慢了,所以选择一个国内的镜像就很有必要,我推荐国内的阿里云镜像。 阿里云镜像:配置很简单,修改conf文件夹下的settings.xml文件,添加如下镜像配置:

<mirrors>
    <mirror>
        <id>alimaven</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <!--  <mirrorOf>central</mirrorOf> -->
        <mirrorOf>central,!getui-nexus,!Guava,!shuncom</mirrorOf>
    </mirror>
</mirrors>

上例子中,mirrorOf的值为central,表示该配置为中央库的镜像,任何对于中央仓库的请求都会转至该镜像,用户也可以用同样的方法配置其他仓库的镜像

这里介绍下<mirrorOf>配置的各种选项

  • <mirrorOf>*<mirrorOf>:匹配所有远程仓库。
  • <mirrorOf>external:*<mirrorOf>:匹配所有远程仓库,使用localhost的除外,使用file://协议的除外。也就是说,匹配所有不在本机上的远程仓库。
  • <mirrorOf>repo1,repo2<mirrorOf>:匹配仓库repo1h和repo2,使用逗号分隔多个远程仓库。
  • <mirrorOf>*,!repo1<mirrorOf>:匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。

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

5. profiles

一个profiles下面可以包含多个profile

<profiles>
    <profile>
        <id>jdRepository</id>
        <repositories>
            <repository>
                <id>jitpack.io</id>
                <url>https://jitpack.io</url>
            </repository>
        </repositories>
    </profile>
</profiles>

6.activeProfiles

<activeProfiles>
    <activeProfile>nexusRepoistory</activeProfile>
    <activeProfile>localRepository</activeProfile>
</activeProfiles>

pom.xml

当我们在项目中需要用到maven帮我们解决jar包依赖问题,帮我们解决项目中的编译、测试、打包、部署时,项目中必须要有pom.xml文件,这些都是依靠pom的配置来完成的。

POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,包含了项目的基本信息,用于描述项目如何构件,声明项目依赖,等等。

执行任务或目标时,Maven 会在当前目录中查找 POM。它读取 POM,获取所需的配置信息,然后执行目标。

POM 中可以指定以下配置:

  • 项目依赖
  • 插件
  • 执行目标
  • 项目构件 profile
  • 项目版本
  • 项目开发者列表
  • 相关邮件列表信息

maven坐标

    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <packaging>jar</packaging>
  • goupId:定义当前构件所属的组,通常与域名反向一一对应。
  • artifactId:项目组中构件的编号。
  • version:当前构件的版本号,每个构件可能会发布多个版本,通过版本号来区分不同版本的构件。
  • package:定义该构件的打包方式,比如我们需要把项目打成jar包,采用java -jar去运行这个jar包,那这个值为jar;若当前是一个web项目,需要打成war包部署到tomcat中,那这个值就是war,可选(jar、war、ear、pom、maven-plugin),比较常用的是jar、war、pom,这些后面会详解。

上面接元素中,groupId、artifactId、version是必须要定义的,package可以省略,默认为jar。

maven导入依赖的构件

maven可以帮我们引入需要依赖的构件(jar等),而maven是如何定位到某个构件的呢?

项目中如果需要使用第三方的jar,我们需要知道其坐标信息,然后将这些信息放入pom.xml文件中的dependencies元素中:

<project>    
    <dependencies>    
        
        <!-- 在这里添加你的依赖 -->   
        <dependency>           
            <groupId></groupId>    
            <artifactId></artifactId>    
            <version></version>   
            <type></type>         
            <scope></scope>          
            <optional></optional>   
            <exclusions>            
                <exclusion></exclusion>   
                <exclusion></exclusion>     
            </exclusions>       
        </dependency>   
        
    </dependencies>
</project>

dependencies元素中可以包含多个dependency,每个dependency就表示当前项目需要依赖的一个构件的信息

  • groupId、artifactId、version:唯一定位一个依赖
  • type:依赖的类型,表示所要依赖的构件的类型,对应于被依赖的构件的packaging。大部分情况下,该元素不被声明,默认值为jar,表示被依赖的构件是一个jar包。
  • scope:依赖的范围
  • option:标记依赖是否可选
  • exclusions:用来排除传递性的依赖

scope 依赖范围

我们的需求:

  1. 我们在开发项目的过程中,可能需要用junit来写一些测试用例,此时需要引入junit的jar包,但是当我们项目部署在线上运行了,测试代码不会再执行了,此时junit.jar是不需要了,所以junit.jar只是在编译测试代码,运行测试用例的时候用到,而上线之后用不到了,所以部署环境中是不需要的
  2. 我们开发了一个web项目,在项目中用到了servlet相关的jar包,然后将其部署在tomcat中,而tomcat中自带了servlet的jar包,那么我们的需求是开发、编译、单元测试的过程中需要这些jar,上线之后,servlet相关的jar由web容器提供,也就是说打包的时候,不需要将servlet相关的jar打入war包了
  3. 像jdbc的驱动,只有在运行的时候才需要,编译的时候是不需要的

这些需求怎么实现呢?

我们都知道,java中编译代码、运行代码都需要用到classpath变量,classpath用来列出当前项目需要依赖的jar包。

maven用到classpath的地方有:编译源码、编译测试代码、运行测试代码、运行项目,这几个步骤都需要用到classpath。

如上面的需求,maven中的scope可以帮我们解决这方面的问题,scope是用来控制被依赖的构件与classpath的关系(编译、打包、运行所用到的classpath),scope有以下几种值:

  • compile:编译依赖范围,如果没有指定,默认使用该依赖范围,对于编译源码、编译测试代码、测试、运行4种classpath都有效
  • test:测试依赖范围,使用此依赖范围的maven依赖,只对编译测试、运行测试的classpath有效,在编译主代码、运行项目时无法使用此类依赖。比如junit,它只有在编译测试代码及运行测试的时候才需要。
  • provided:已提供依赖范围。表示项目的运行环境中已经提供了所需要的构件,对于此依赖范围的maven依赖,对于编译源码、编译测试、运行测试中classpath有效,但在运行时无效。比如servlet-api,这个在编译和测试的时候需要用到,但是在运行的时候,web容器已经提供了,就不需要maven帮忙引入了。
  • runtime:运行时依赖范围,使用此依赖范围的maven依赖,对于编译测试、运行测试和运行项目的classpath有效,但在编译主代码时无效,比如jdbc驱动实现,运行的时候才需要具体的jdbc驱动实现。
  • system:系统依赖范围,该依赖与3种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示指定依赖文件的路径。这种依赖直接依赖于本地路径中的构件,可能每个开发者机器中构件的路径不一致,所以如果使用这种写法,你的机器中可能没有问题,别人的机器中就会有问题,可能构成构建的不可移植,所以建议谨慎使用。systemPath元素可以引用环境变量,如:
<dependency>
    <groupId>javax.sql</groupId>
    <artifactId>jdbc-stdext</artifactId>
    <Version>2.0</Version>
    <scope>system</scope>
    <systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
  • import:导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。 上述除import以外的各种依赖范围与三种classpath的关系如下:

依赖范围与classpath的关系如下:

依赖范围编译源码编译测试代码运行测试运行项目示例
compileYYYYspring-web
testYYjunit
provideYYYservlet-api
runtimeYYYjdbc驱动
systemYYY本地的jar包

scope如果对于运行范围有效,意思是指依赖的jar包会被打包到项目的运行包中,最后运行的时候会被添加到classpath中运行

如果scope对于运行项目无效,那么项目打包的时候,这些依赖不会被打包到运行包中

传递性依赖

不过上面我们说的scope元素的值会对这种传递依赖会有影响。

假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,而A对于C是传递性依赖,而第一直接依赖的scope和第二直接依赖的scope决定了传递依赖的范围,即决定了A对于C的scope的值。

下面我们用表格来列一下这种依赖的效果,表格最左边一列表示第一直接依赖(即A->B的scope的值),而表格中的第一行表示第二直接依赖(即B->C的scope的值),行列交叉的值显示的是A对于C最后产生的依赖效果。

compiletestprovidedruntime
compilecompile--runtime
testtest--test
providedprovided--provided
runtimeruntime--runtime

解释一下:

  1. 比如A->B的scope是compile,而B->C的scope是test,那么按照上面表格中,对应第2行第3列的值-,那么A对于C是没有依赖的,A对C的依赖没有从B->C传递过来,所以A中是无法使用C的
  2. 比如A->B的scope是compile,而B->C的scope是runtime,那么按照上面表格中,对应第2行第5列的值为runtime,那么A对于C是的依赖范围是runtime,表示A只有在运行的时候C才会被添加到A的classpath中,即对A进行运行打包的时候,C会被打包到A的包中
  3. 大家仔细看一下,上面的表格是有规律的,当B->C依赖是compile的时候(表中第2列),那么A->C的依赖范围和A->B的sope是一样的;当B->C的依赖是test时(表中第3列),那么B->C的依赖无法传递给A;当B->C的依赖是provided(表第4列),只传递A->C的scope为provided的情况,其他情况B->C的依赖无法传递给A;当B->C的依赖是runtime(表第5列),那么C按照B->C的scope传递给A
  4. 上面表格大家多看几遍,理解理解

maven依赖调解功能

现实中可能存在这样的情况,A->B->C->Y(1.0),A->D->Y(2.0),此时Y出现了2个版本,1.0和2.0,此时maven会选择Y的哪个版本?

解决这种问题,maven有2个原则:

路径最近原则

上面A->B->C->Y(1.0),A->D->Y(2.0),Y的2.0版本距离A更近一些,所以maven会选择2.0。

但是如果出现了路径是一样的,如:A->B->Y(1.0),A->D->Y(2.0),此时maven又如何选择呢?

最先声明原则

如果出现了路径一样的,此时会看A的pom.xml中所依赖的B、D在dependencies中的位置,谁的声明在最前面,就以谁的为主,比如A->B在前面,那么最后Y会选择1.0版本。

可选依赖(optional元素)

有这么一种情况:

A->B中scope:compile
B->C中scope:compile

按照上面介绍的依赖传递性,C会传递给A,被A依赖。

假如B不想让C被A自动依赖,可以怎么做呢?

dependency元素下面有个optional,是一个boolean值,表示是一个可选依赖,B->C时将这个值置为true,那么C不会被A自动引入。

<optional>true</optional>

排除依赖

有时候你引入的依赖中包含你不想要的依赖包,你想引入自己想要的,这时候就要用到排除依赖了,比如下图中spring-boot-starter-web自带了logback这个日志包,我想引入log4j2的,所以我先排除掉logback的依赖包,再引入想要的包就行了

image.png

排除依赖代码结构:

<exclusions>
    <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </exclusion>
</exclusions>

这里注意:声明exclustion的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。

上面使用使用exclusions元素排除了B->C依赖的传递,也就是B->C不会被传递到A中。

exclusions中可以有多个exclusion元素,可以排除一个或者多个依赖的传递

maven仓库

仓库是一个位置,这个位置是用来存放各种第三方构件的,所有maven项目可以共享这个仓库中的构件。Maven 仓库能帮助我们管理构件(主要是jar包),它就是放置所有jar文件(jar、war、zip、pom等等)的地方。

仓库的分类

主要分为2大类:

  1. 本地仓库
  2. 远程仓库

而远程仓库又分为:中央仓库、私服、其他公共远程仓库

当maven根据坐标寻找构件的时候,会

  1. 首先查看本地仓库,如果本地仓库存在,则直接使用;
  2. 如果本地不存在,maven会去远程仓库中查找,如果找到了,会将其下载到本地仓库中进行使用,
  3. 如果本地和远程仓库都没有找到构件,maven会报错,构件只有在本地仓库中存在了,才能够被maven项目使用。

本地仓库

默认情况下,maven本地仓库默认地址是~/.m2/respository目录,这个默认我们也可以在~/.m2/settings.xml文件中进行修改:

<localRepository>本地仓库地址</localRepository>

当我们使用maven的时候,依赖的构件都会从远程仓库下载到本地仓库目录中。

Maven 的本地仓库,在安装 Maven 后并不会创建,当我们执行第一条 maven 命令的时候本地仓库才会创建,此时会从远程仓库下载构件到本地仓库给maven项目使用。

远程仓库

最开始我们使用maven的时候,本地仓库中的构件是空的,此时maven必须要提供一种功能,要能够从外部获取这些构件,这个外部就是所谓的远程仓库,远程仓库可以有多个,当本地仓库找不到构件时,可以去远程仓库找,然后放置到本地仓库中进行使用。

中央仓库

由于maven刚安装好的时候,本地仓库是空的,此时我们什么都没有配置,去执行maven命令的时候,我们会看到maven默认执行了一些下载操作,这个下载地址就是中央仓库的地址,这个地址是maven社区为我们提供的,是maven内置的一个默认的远程仓库地址,不需要用户去配置。中央仓库中包含了这个世界上大多数流行的开源java构件,基本上所有的jave开发者都会使用这个仓库,一般我们需要的第三方构件在这里都可以找到。

repo.maven.apache.org/maven2

search.maven.org/

私服

私服也是远程仓库中的一种,我们为什么需要私服呢?

如果我们一个团队中有几百个人在开发一些项目,都是采用maven的方式来组织项目,那么我们每个人都需要从远程仓库中把需要依赖的构件下载到本地仓库,这对公司的网络要求也比较高,为了节省这个宽带和加快下载速度,我们在公司内部局域网内部可以架设一台服务器,这台服务器起到一个代理的作用,公司里面的所有开发者去访问这个服务器,这台服务器将需要的构件返回给我们,如果这台服务器中也没有我们需要的构件,那么这个代理服务器会去远程仓库中查找,然后将其先下载到代理服务器中,然后再返回给开发者本地的仓库。

还有公司内部有很多项目之间会相互依赖,你可能是架构组的,你需要开发一些jar包给其他组使用,此时,我们可以将自己jar发布到私服中给其他同事使用,如果没有私服,可能需要我们手动发给别人或者上传到共享机器中,不过管理起来不是很方便。

目前常用的搭建 Maven 私服的服务器有 3 台:Apache 基金会的 Archiva、JFrog 的 Artifactory 和 Sonatype 的 Nexus。最流行的当然是Nexus。

更多关于私服的内容请参考:www.itsoku.com/course/2/66

总体上来说私服有以下好处:

  1. 加速maven构件的下载速度
  2. 节省宽带
  3. 方便部署自己的构件以供他人使用
  4. 提高maven的稳定性,中央仓库需要本机能够访问外网,而如果采用私服的方式,只需要本机可以访问内网私服就可以了

其他远程仓库

中央仓库是在国外的,访问速度不是特别快,所以有很多比较大的公司做了一些好事,自己搭建了一些maven仓库服务器,公开出来给其他开发者使用,比如像阿里、网易等等,他们对外提供了一些maven仓库给全球开发者使用,在国内的访问速度相对于maven中央仓库来说还是快了不少。

还有一些公司比较牛,只在自己公开的仓库中发布构件,这种情况如果要使用他们的构件时,需要去访问他们提供的远程仓库地址。

maven配置远程仓库

有两种方式:

  1. 在pom.xml中配置repositories,这种方式只对当前项目起效,如果我们需要对所有项目起效,我们可以使用方式2
<!--配置远程仓库-->
<repositories>
    <!--在repositories元素下,可以使用repository子元素声明一个或者多个远程仓库-->
    <repository>
        <!--远程仓库的一个标识,中央仓库的id是central,所以添加远程仓库的时候,id不要和中央仓库的id重复,会把中央仓库的覆盖掉-->
        <id>aliyun-releases</id>
        <!--远程仓库地址-->
        <url>https://maven.aliyun.com/repository/public</url>
        
        <!--主要用来配置是否需要从这个远程仓库下载稳定版本构件-->
        <releases>
            <!--默认为true,表示是否需要从这个远程仓库中下载稳定版本或者快照版本的构件,一般使用第三方的仓库,都是下载稳定版本的构件-->
            <enabled>true</enabled>
        </releases>
        <!--主要用来配置是否需要从这个远程仓库下载快照版本构件-->
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
  1. settings.xml中进行配置镜像mirror,对所有使用该配置的maven项目起效

如果仓库X可以提供仓库Y所有的内容,那么我们就可以认为X是Y的一个镜像,通俗点说,可以从Y获取的构件都可以从他的镜像中进行获取。

<!-- mirrors:镜像列表,可以用来从远程仓库下载jar包。可以有多个mirror元素,每个mirror表示一个远程镜像-->
<mirrors>
    <mirror>
        <!--镜像的id,是一个标识-->
        <id>alimaven</id>
        <!--镜像的名称,这个相当于一个描述信息,方便大家查看-->
        <name>aliyun maven</name>
        <!--镜像对应的远程仓库的地址-->
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <!--  <mirrorOf>central</mirrorOf> -->
        <!--指定哪些远程仓库的id使用这个镜像,这个对应pom.xml文件中repository元素的id,
        就是表示这个镜像是给哪些pom.xml中的远程仓库使用的,
        这里面需要列出远程仓库的id,多个之间用逗号隔开,*表示给所有远程仓库做镜像-->
        <mirrorOf>central,!getui-nexus,!Guava,!shuncom</mirrorOf>
    </mirror>
</mirrors>

pom.xml文件的repository元素中有个id,这个id就是远程仓库的id,而mirrorOf就是用来配置哪些远程仓库会走这个镜像去下载构件 更多关于mirrorOf的用法如下:

匹配所有远程仓库id,这些远程仓库都会走这个镜像下载构件
<mirrorOf>*</mirrorOf>

上面匹配指定的仓库,这些指定的仓库会走这个镜像下载构件
<mirrorOf>远程仓库1的id,远程仓库2的id</mirrorOf>

上面匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中移除
<mirrorOf>*,!repo1</mirrorOf>

关于镜像一个比较常用的用法是结合私服一起使用,由于私服可以代理所有远程仓库(包含中央仓库),因此对于maven用来来说,只需通过访问一个私服就可以间接访问所有外部远程仓库了。

参考:

www.itsoku.com/course/2/62

juejin.cn/post/708722…

juejin.cn/post/684490…

mp.weixin.qq.com/mp/appmsgal…