Maven 最佳实践:Maven 配置使用第三方仓库的几种方法

什么是 Maven 仓库

在不用 Maven 的时候,比如说以前我们用 Ant 构建项目,在项目目录下,往往会看到一个名为 /lib 的子目录,那里存放着各类第三方依赖 jar 文件,如 log4j.jar,junit.jar 等等。每建立一个项目,你都需要建立这样的一个 /lib 目录,然后复制一对 jar 文件,这是很明显的重复。重复永远是噩梦的起点,多个项目不共用相同的 jar 文件,不仅会造成磁盘资源的浪费,也使得版本的一致性管理变得困难。此外,如果你使用版本管理工具,如 SVN(你没有使用版本管理工具?马上试试 SVN 吧,它能帮你解决很多头疼的问题),你需要将大量的 jar 文件提交到代码库里,可是版本管理工具在处理二进制文件方面并不出色。

Maven 仓库就是放置所有 JAR 文件(WAR,ZIP,POM 等等)的地方,所有 Maven 项目可以从同一个 Maven 仓库中获取自己所需要的依赖 JAR,这节省了磁盘资源。此外,由于 Maven 仓库中所有的 JAR 都有其自己的坐标,该坐标告诉 Maven 它的组 ID,构件 ID,版本,打包方式等等,因此 Maven 项目可以方便的进行依赖版本管理。你也不在需要提交 JAR 文件到 SCM 仓库中,你可以建立一个组织层次的 Maven 仓库,供所有成员使用。

简言之,Maven 仓库能帮助我们管理构件(主要是 JAR)。

本地仓库 vs. 远程仓库

运行 Maven 的时候,Maven 所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件。

比如说,你的项目配置了 junit-3.8 的依赖,在你运行 mvn test 的时候,Maven 需要使用 junit-3.8 的 jar 文件,它首先根据坐标查找本地仓库,如果找到,就直接使用。如果没有,Maven 会检查可用的远程仓库配置,然后逐个尝试这些远程仓库去下载 junit-3.8 的 jar 文件,如果远程仓库存在该文件,Maven 会将其下载到本地仓库中,继而使用。如果尝试过所有远程仓库之后,Maven 还是没能够下载到该文件,它就会报错。

Maven 缺省的本地仓库地址为 ${user.home}/.m2/repository。也就是说,一个用户会对应的拥有一个本地仓库。

你也可以自定义本地仓库的位置,修改 ${user.home}/.m2/settings.xml

<settings>  
  ...  
  <localRepository>D:\java\repository</localRepository>  
  ...  
</settings>  

你还可以在运行时指定本地仓库位置:

mvn clean install -Dmaven.repo.local=/home/juven/myrepo/

还有一点需要理解的是,当我们运行 install 的时候,Maven 实际上是将项目生成的构件安装到了本地仓库,也就是说,只有 install 了之后,其它项目才能使用此项目生成的构件。

了解了本地仓库,接着了解一下 Maven 缺省的远程仓库,即 Maven 中央仓库。

安装好 Maven 之后,我们可以建立一个简单的项目,配置一些简单的依赖,然后运行 mvn clean install,项目就构建好了。我们没有手工的去下载任何 jar 文件,这一切都是因为 Maven 中央仓库的存在,当 Maven 在本地仓库找不到需要的 jar 文件时,它会查找远程仓库,而一个原始的 Maven 安装就自带了一个远程仓库—— Maven 中央仓库。

这个 Maven 中央仓库是在哪里定义的呢?在我的机器上,我安装了 maven-2.0.10,我可以找到这个文件:${M2_HOME}/lib/maven-2.0.10-uber.jar,打开该文件,能找到超级 POM:\org\apache\maven\project\pom-4.0.0.xml,它是所有 Maven POM 的父 POM,所有 Maven 项目继承该配置,你可以在这个 POM 中发现如下配置:

<repositories>  
  <repository>  
    <id>central</id>  
    <name>Maven Repository Switchboard</name>  
    <layout>default</layout>  
    <url>http://repo1.maven.org/maven2</url>  
    <snapshots>  
      <enabled>false</enabled>  
    </snapshots>  
  </repository>  
</repositories>  

关于远程仓库的配置,下面的小节我会详细解释,这里我们只要知道,中央仓库的 id 为 central,远程 url 地址为 http://repo1.maven.org/maven2, 它关闭了 snapshot 版本构件下载的支持。

在POM中配置远程仓库

前面我们看到超级 POM 配置了 ID 为 central 的远程仓库,我们可以在 POM 中配置其它的远程仓库。这样做的原因有很多,比如你有一个局域网的远程仓库,使用该仓库能大大提高下载速度,继而提高构建速度,也有可能你依赖的一个 jar 在 central 中找不到,它只存在于某个特定的公共仓库,这样你也不得不添加那个远程仓库的配置。

这里我配置一个远程仓库指向中央仓库的中国镜像:

<project>  
...  
  <repositories>  
    <repository>  
      <id>maven-net-cn</id>  
      <name>Maven China Mirror</name>  
      <url>http://maven.net.cn/content/groups/public/</url>  
      <releases>  
        <enabled>true</enabled>  
      </releases>  
      <snapshots>  
        <enabled>false</enabled>  
      </snapshots>  
    </repository>  
  </repositories>  
  <pluginRepositories>  
    <pluginRepository>  
      <id>maven-net-cn</id>  
      <name>Maven China Mirror</name>  
      <url>http://maven.net.cn/content/groups/public/</url>  
      <releases>  
        <enabled>true</enabled>  
      </releases>  
      <snapshots>  
        <enabled>false</enabled>  
      </snapshots>      
    </pluginRepository>  
  </pluginRepositories>  
...  
</project>  

我们先看一下 <repositories> 的配置,你可以在它下面添加多个 <repository>,每个 <repository> 都有它唯一的 ID,一个描述性的 name,以及最重要的,远程仓库的 url。此外,<releases><enabled>true</enabled></releases> 告诉 Maven 可以从这个仓库下载 releases 版本的构件,而 <snapshots><enabled>false</enabled></snapshots> 告诉 Maven 不要从这个仓库下载 snapshot 版本的构件。禁止从公共仓库下载 snapshot 构件是推荐的做法,因为这些构件不稳定,且不受你控制,你应该避免使用。当然,如果你想使用局域网内组织内部的仓库,你可以激活 snapshot 的支持。

关于 <repositories> 的更详细的配置及相关解释,请参考:http://www.sonatype.com/books/maven-book/reference_zh/apas02s08.html。

至于 <pluginRepositories>,这是配置 Maven 从什么地方下载插件构件(Maven的所有实际行为都由其插件完成)。该元素的内部配置和 <repository> 完全一样,不再解释。

在settings.xml中配置远程仓库

我们知道了如何在 POM 中配置远程仓库,但考虑这样的情况:在一个公司内部,同时进行这 3 个项目,而且以后随着这几个项目的结束,越来越多的项目会开始;同时,公司内部建立一个 Maven 仓库。我们统一为所有这些项目配置该仓库,于是不得不为每个项目提供同样的配置。问题出现了,这是重复 !

其实我们可以做到只配置一次,在哪里配置呢?就是 settings.xml

不过事情没有那么简单,不是简单的将 POM 中的 <repositories><pluginRepositories> 元素复制到 settings.xml 中就可以, setting.xml 不直接支持 这两个元素。但我们还是有一个并不复杂的解决方案,就是利用 profile,如下:

<settings>
  ...
  <profiles>
    <profile>
      <id>dev</id>
      <!-- repositories and pluginRepositories here-->
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>dev</activeProfile>
  </activeProfiles>
  ...
</settings>

这里我们定义一个 id 为 dev 的 profile,将所有 repositories 以及 pluginRepositories 元素放到这个 profile 中,然后,使用 <activeProfiles> 元素自动激活该 profile。这样,你就不用再为每个 POM 重复配置仓库。

使用 profile 为 settings.xml 添加仓库提供了一种用户全局范围的仓库配置。

镜像

如果你的地理位置附近有一个速度更快的 central 镜像,或者你想覆盖 central 仓库配置,或者你想为所有 POM 使用唯一的一个远程仓库(这个远程仓库代理的所有必要的其它仓库),你可以使用 settings.xml 中的 mirror 配置。

以下的 mirror 配置用 maven.net.cn 覆盖了 Maven 自带的 central:

<settings>  
...  
  <mirrors>  
    <mirror>  
      <id>maven-net-cn</id>  
      <name>Maven China Mirror</name>  
      <url>http://maven.net.cn/content/groups/public/</url>  
      <mirrorOf>central</mirrorOf>  
    </mirror>  
  </mirrors>  
...  
</settings>

这里唯一需要解释的是 <mirrorOf>,这里我们配置 central 的镜像,我们也可以配置一个所有仓库的镜像,以保证该镜像是 Maven 唯一使用的仓库:

<settings>  
...  
  <mirrors>  
    <mirror>  
      <id>my-org-repo</id>  
      <name>Repository in My Orgnization</name>  
      <url>http://192.168.1.100/maven2</url>  
      <mirrorOf>*</mirrorOf>  
    </mirror>  
  </mirrors>  
...  
</settings>  

关于更加高级的镜像配置,可以参考:http://maven.apache.org/guides/mini/guide-mirror-settings.html

分发构件至远程仓库

mvn install 会将项目生成的构件安装到本地 Maven 仓库,mvn deploy 用来将项目生成的构件分发到远程 Maven 仓库。本地 Maven 仓库的构件只能供当前用户使用,在分发到远程 Maven 仓库之后,所有能访问该仓库的用户都能使用你的构件。

我们需要配置 POM 的 distributionManagement 来指定 Maven 分发构件的位置,如下:

<project>    
  ...    
  <distributionManagement>    
    <repository>    
      <id>nexus-releases</id>    
      <name>Nexus Release Repository</name>    
      <url>http://127.0.0.1:8080/nexus/content/repositories/releases/</url>    
    </repository>    
    <snapshotRepository>    
      <id>nexus-snapshots</id>    
      <name>Nexus Snapshot Repository</name>    
      <url>http://127.0.0.1:8080/nexus/content/repositories/snapshots/</url>    
    </snapshotRepository>    
  </distributionManagement>    
  ...    
</project>

Maven 区别对待 release 版本的构件和 snapshot 版本的构件,snapshot 为开发过程中的版本,实时,但不稳定,release 版本则比较稳定。Maven 会根据你项目的版本来判断将构件分发到哪个仓库。

一般来说,分发构件到远程仓库需要认证,如果你没有配置任何认证信息,你往往会得到 401 错误。这个时候,如下在 settings.xml 中配置认证信息:

<settings>    
  ...    
  <servers>    
    <server>    
      <id>nexus-releases</id>    
      <username>admin</username>    
      <password>admin123</password>    
    </server>    
    <server>    
      <id>nexus-snapshots</id>    
      <username>admin</username>    
      <password>admin123</password>    
    </server>      
  </servers>    
  ...    
</settings>

需要注意的是,settings.xml 中 server 元素下 id 的值必须与 POM 中 repository 或 snapshotRepository 下 id 的值完全一致。将认证信息放到 settings 下而非 POM 中,是因为 POM 往往是它人可见的,而 settings.xml 是本地的。

小结

本文介绍了 Maven 仓库,它是什么?本地仓库,远程仓库,中央仓库具体是指什么?并介绍了如何在 POM 中配置项目层次的仓库,在 settings 中配置用户层次的仓库,以及 mirror。本文还介绍了如何安装构件到本地仓库,如何分发构件至仓库。

如果觉得这对你有用,请随意赞赏,给与作者支持
评论 0
最新评论