最近在接触公司内一个Maven项目需要配置公司内部的私有仓库,可以借此机会熟悉下Java开发大名鼎鼎的Maven,网上不少关于Maven的配置资料都是基于Windows环境,基于OSX和Linux的教程很少,因此在环境搭建及配置中也遇到不少问题,在成功搭建这个环境过程中也学到了不少东西,借此记录。
1. Maven安装及配置
- 下载Maven,将下载文件解压后得到的文件夹(apache-maven-x.x.x)放在某个位置,如:
/Users/lijiahao/apache-maven-3.3.9
。 - 添加Maven环境变量,在
~/.bash_profile
内添加如下:
export M2_HOME=/Users/lijiahao/apache-maven-3.3.9
export PATH=$PATH:$M2_HOME/bin
- 重新加载
~/.bash_profile
并生效,此时全局命令就可以使用mvn
了:
$ source ~/.bash_profile
$ mvn -v
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)
Maven home: /Users/lijiahao/apache-maven-3.3.9
Java version: 1.8.0_112, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"
2. 配置私有仓库
maven的配置文件存放在/Users/lijiahao/apache-maven-3.3.9/conf/settings.xml
文件下,原始的xml文件内容很简单,每一项都有官方的详细注释:
<?xml version="1.0" encoding="UTF-8"?>
<!--
| 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.home}/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.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
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</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>
-->
</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>
-->
</profiles>
<!-- activeProfiles
| List of profiles that are active for all builds.
|
<activeProfiles>
<activeProfile>alwaysActiveProfile</activeProfile>
<activeProfile>anotherAlwaysActiveProfile</activeProfile>
</activeProfiles>
-->
</settings>
在原有settings.xml
的基础上,为了能达到可以访问私有仓库的目的,几个关键字段如下:
repositories
、mirrors
,指定私服仓库:
<repositories>
<repository>
<id>nexus</id>
<url>https://127.0.0.0:8440/repository/maven-public/</url>
</repository>
</repositories>
Maven根据如上配置,会自动从私有仓库中下载构件,但是,如果私服中没有对应构件,还是会访问中央仓库,但是我们在Maven构件项目时,只是希望本地构件Maven项目时,仅仅只是访问Nexus,不希望访问中央仓库,访问中央仓库应该是Nexus的事,不是我们本地Maven配置的事,为了达到这个目标,我们还需要配置镜像:
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>https://127.0.0.0:8443/repository/maven-public/</url>
</mirror>
</mirrors>
通过镜像配置,创建一个匹配任何仓库的镜像,镜像的地址变为私服,这样Maven对于构件的请求都会转到Nexus中去,没有构件,私有仓库自己访问中央仓库缓存,本地只需要从私有仓库下载构件即可。
完整的settings.xml
配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 将maven的本地下载仓库重新指向新目录,默认值是 ${user.home}/.m2/repository -->
<localRepository>/Users/lijiahao/apache-maven-3.3.9/repos</localRepository>
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
<server>
<id>nexus</id>
<username>test</username>
<password>test</password>
</server>
</servers>
<mirrors>
<mirror>
<id>nexus</id>
<!-- 所有的构建均从私有仓库中下载,*代表所有,也可以写成central -->
<mirrorOf>*</mirrorOf>
<url>https://127.0.0.0:8443/repository/maven-public/</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>central</id>
<url>http://central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<!-- 激活 -->
<activeProfiles>
<activeProfile>nexus</activeProfile>
</activeProfiles>
</settings>
3. 解决私有仓库构件无法下载问题
基本上安装了maven和配置了settings.xml之后就可以直接在maven项目的执行相关操作了,但是具体操作时发现,执行mvn
命令时会报错:
$ mvn clean
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.706 s
[INFO] Finished at: 2019-12-12T15:16:05+08:00
[INFO] Final Memory: 9M/123M
[INFO] ------------------------------------------------------------------------
[ERROR] Plugin org.apache.maven.plugins:maven-clean-plugin:2.5 or one of its dependencies could not be resolved:
...
...
根据报错信息可以得知是相关依赖缺失,导致编译失败,检查settings.xml
下的localRepository
配置的目录,发现依赖确实没有下载下来,那是什么原因呢?我先把镜像换成maven官方地址:
<mirror>
<id>central</id>
<name>Maven Repository Switchboard</name>
<url>http://repo1.maven.org/maven2/</url>
<mirrorOf>central</mirrorOf>
</mirror>
重新执行mvn clean
,发现可以正常安装构件,再仔细对比官方地址与私有仓库的地址,发现私有仓库地址为https
开头,之后通过查询maven官方得到答案:如果远程服务器为HTTPS,还需要额外的验证信息,也就是需要导入私有仓库的证书,才能从私有仓库下载构件。在使用NEXUS
进行私有仓库搭建时会有一个nexus.crt
的证书,我们接下来需要把证书导入到我们本地的Java环境中:
我当前的jdk版本为1.8.0
:
$ echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home
使用keytool导入证书到jdk安装目录下的cacerts
中,这里我对证书使用了一个名为server的别名:
$ sudo keytool -import -file ./nexus.crt -keystore $JAVA_HOME/jre/lib/security/cacerts -alias server
Enter keystore password:
注:
- 是否需要sudo根据jdk文件权限而定,如果没有直接读写权限,则需要使用sudo;
- osx下在提示输入秘钥库口令时,输入的秘钥不是root的密码,而是changeit,否则会提示
keytool 错误: java.io.IOException: Keystore was tampered with, or password was incorrect
错误,Linux下暂时还没验证此问题。
导入证书后,查询证书是否正确导入:
$ keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts | grep server
输入密钥库口令: changeit
server, 2019-12-12, trustedCertEntry,
导入证书后再重新执行mvn clean
,发现依赖已经可以正常安装,mvn命令已经可以正常使用。
总结
通过此次实践,我对Maven的理解是:Maven是Java开发的项目管理工具,跟nodejs中最常用的NPM
类似,都是通过项目下一个配置文件(node环境里就是package.json,Java里就是pom.xml)来管理项目下需要使用的依赖,区别在于:
- Java中的Maven仓库在开发者电脑上是全局的,所有项目的依赖都集中存放在本地仓库中,其实跟Python的PIP管理很像;
- Node中的依赖如果你不写package.json,那么依赖的就是全局的库;如果写了package.json,就会把所有依赖下载到node_modules文件夹,在node项目中可以轻松修改第三方依赖的源码,可以很容易该出自己特制的三方依赖;
- 如果Java项目中需要其他特有版本的依赖,需要单独在指定版本,至于如果需要修改构件源码,这个需要后续进一步学习了,或许对于Java而言,第三方构件属于比较严谨的库,一般不会进行修改,这可能也是Maven当初设计的初衷。
(完)