Maven

Stone大约 34 分钟

Maven

Maven 是一个项目管理和自动化构建工具,主要用于 Java 项目,但也可以用于其他语言的项目。以下是 Maven 的主要特点:

  • 项目构建和依赖管理:Maven 可以自动下载、编译、测试和打包 Java 项目。它还可以管理项目的依赖关系,即项目所依赖的其他库和框架。通过使用 Maven,开发人员可以轻松地定义项目所需的所有依赖项,Maven 会自动从中央仓库或指定的仓库中下载这些依赖项。
  • POM(Project Object Model):Maven 使用一个名为 POM(项目对象模型)的 XML 文件来配置项目。这个文件包含了项目的所有信息,如项目的名称、版本、依赖项、构建配置等。通过编辑 POM 文件,开发人员可以轻松地配置项目的各个方面。
  • 构建生命周期:Maven 有一个标准的构建生命周期,包括清理(Clean)、编译(Compile)、测试(Test)、打包(Package)和安装(Install)等阶段。每个阶段都可以执行特定的任务,如编译源代码、运行单元测试、生成 JAR 或 WAR 文件等。开发人员可以自定义这些阶段的行为,以满足项目的特定需求。
  • 插件系统:Maven 有一个强大的插件系统,可以扩展其功能。插件可以用于执行各种任务,如生成文档、运行代码分析工具、发布项目到远程仓库等。Maven 提供了许多官方插件,并且还有大量的第三方插件可供选择。
  • 中央仓库:Maven 中央仓库是一个包含了大量 Java 库和框架的在线仓库。开发人员可以在这个仓库中搜索和下载所需的依赖项。此外,开发人员还可以将自己的项目发布到中央仓库或私有仓库中,以便其他人使用。
  • 跨平台:Maven 是跨平台的,可以在 Windows、Linux 和 MacOS 等操作系统上运行。这使得开发人员可以在不同的环境中一致地构建和测试项目。
  • 约定优于配置:Maven 遵循“约定优于配置”的原则,这意味着它有一组默认的目录结构和构建配置。这使得新开发人员可以更快地了解项目的结构和工作方式,并减少了配置错误的可能性。

总之,Maven 是一个功能强大的项目管理和自动化构建工具,可以显著提高 Java 项目的开发效率和质量。

image-20240526193743437

安装

在安装 Maven 前,需要先安装 Java。

Windows

官方网站open in new window下载以 .zip 结尾的安装包,然后解压即完成安装。

安装后还需要配置环境变量,包括 JAVA_HOME,MAVEN_HOME 以及 PATH。

配置完成后进行测试:

C:\Users\stone>java -version
java version "17.0.11" 2024-04-16 LTS
Java(TM) SE Runtime Environment (build 17.0.11+7-LTS-207)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.11+7-LTS-207, mixed mode, sharing)

C:\Users\stone>mvn -v
Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
Maven home: D:\software\apache-maven-3.9.6
Java version: 17.0.11, vendor: Oracle Corporation, runtime: D:\software\jdk-17.0.11
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Linux

官方网站open in new window下载以 .tar.gz 结尾的安装包,然后解压即完成安装。

[root@stone ~]# tar -xvzf apache-maven-3.9.7-bin.tar.gz

安装后还需要配置环境变量,包括 JAVA_HOME,MAVEN_HOME 以及 PATH。

[root@stone ~]# vi .bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin

export PATH

export JAVA_HOME=/root/jdk-17.0.11
export CLASSPATH=.:${JAVA_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

#增加如下环境变量
export MAVEN_HOME=/root/apache-maven-3.9.7
export PATH=${MAVEN_HOME}/bin:$PATH

配置完成后进行测试:

[root@stone ~]# source .bash_profile
[root@stone ~]# java -version
java version "17.0.11" 2024-04-16 LTS
Java(TM) SE Runtime Environment (build 17.0.11+7-LTS-207)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.11+7-LTS-207, mixed mode, sharing)
[root@stone ~]# mvn -v
Apache Maven 3.9.7 (8b094c9513efc1b9ce2d952b3b9c8eaedaf8cbf0)
Maven home: /root/apache-maven-3.9.7
Java version: 17.0.11, vendor: Oracle Corporation, runtime: /root/jdk-17.0.11
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-1160.el7.x86_64", arch: "amd64", family: "unix"

仓库

Maven 仓库是项目引入资源的存储位置。在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。Maven 仓库能帮助我们管理这些构件(主要是 JAR),是放置所有 JAR 文件(WAR、ZIP、POM 等)的地方。

Maven仓库有三种类型:

  • 本地仓库:
    • 在安装 Maven 后并不会立即创建,而是在第一次执行 Maven 命令时才会被创建。
    • Maven 所需要的任何构件都是首先从本地仓库获取的。如果本地仓库没有,它会尝试从远程仓库下载构件至本地仓库,然后再使用。
    • 默认情况下,本地仓库位于每个用户的用户目录下的 .m2/repository/ 路径(在Windows上可能位于 %USER_HOME%/.m2/repository/)。
    • 可以通过修改Maven配置文件(settings.xml)中的 <localRepository> 标签来更改本地仓库的默认位置。
  • 中央仓库:
    • 是由 Maven 社区提供的仓库,其中包含了大量常用的库。
    • 中央仓库包含了绝大多数流行的开源 Java 构件,以及源码、作者信息、SCM 信息、许可证信息等。
    • 简单的 Java 项目依赖的构件通常都可以在中央仓库中找到。
  • 远程仓库(私服):
    • 除了中央仓库外,还可以配置其他的远程仓库作为项目依赖的来源。
    • 这些远程仓库可能由公司或组织自己维护,包含了一些特定的或私有的构件。

设置和使用 Maven 仓库:

  • 设置本地仓库:通过修改 Maven 配置文件(settings.xml)中的 <localRepository> 标签来设置本地仓库的位置。
  • 设置镜像仓库:为了提高下载速度,可以在 settings.xml 文件中添加阿里云 Maven 仓库的镜像配置。
  • 设置远程仓库:在 settings.xml 或项目的 pom.xml 文件中,可以通过 <repositories><pluginRepositories> 标签来配置远程仓库的地址和访问权限等信息。

配置

安装完成后,需要编辑 MAVEN_HOME/conf/settings.xml 文件对 Maven 进行配置。

以下是 settings.xml 配置文件的主要部分的详解:

  • <settings>:这是 settings.xml 文件的根元素。

  • <localRepository>:指定本地仓库的路径。Maven 在下载依赖时,会将它们保存在本地仓库中,以便后续快速访问。如果不配置这个元素,Maven 将使用用户目录下的 .m2/repository 目录作为本地仓库。

  • <interactiveMode>:定义 Maven 是否在需要输入时与用户交互。默认为 true,意味着 Maven 会提示用户输入。

  • <usePluginRegistry>:定义 Maven 是否使用插件注册表。默认为 false

  • <offline>:定义 Maven 是否在离线模式下运行。如果设置为 true,Maven 将不会从远程仓库下载任何依赖或插件。

  • <pluginGroups>:指定 Maven 在命令行中不需要提供 groupId 就可以使用的插件组。

  • <servers>:配置仓库认证信息,如用户名和密码。这些信息通常用于访问私有仓库。

    • <id>: 仓库的标识符。

    • <username>: 访问仓库的用户名。

    • <password>: 访问仓库的密码。

    • <privateKey>: 用于仓库访问的私钥路径(如果仓库需要)。

    • <passphrase>: 私钥的密码(如果仓库需要)。

  • <mirrors>:配置镜像仓库,用于替换中央仓库或其他远程仓库。

    • <id>: 镜像的标识符,用于与 <repositories><pluginRepositories> 中的仓库匹配。

    • <url>: 镜像仓库的 URL。

    • <mirrorOf>: 被镜像的仓库的标识符,可以是一个或多个,以逗号分隔。

  • <proxies>:配置 Maven 用于连接到远程仓库的代理服务器。

    • <id>: 代理的标识符。

    • <active>: 是否激活这个代理。

    • <protocol>: 代理使用的协议,如 http 或 https。

    • <host>: 代理服务器的主机名或 IP 地址。

    • <port>: 代理服务器的端口号。

    • <username>: 访问代理服务器的用户名(如果需要)。

    • <password>: 访问代理服务器的密码(如果需要)。

    • <nonProxyHosts>: 不需要代理的主机或域名列表,以竖线 \| 分隔。

  • <profiles>:定义构建配置的个人资料(Profile)。个人资料可以在 Maven 命令行中使用 -P 参数激活,或者在 settings.xml 中通过 <activeProfiles> 元素激活。

    • <id>: 个人资料的标识符。

    • <repositories>: 定义个人资料中使用的仓库列表。

    • <pluginRepositories>: 定义个人资料中使用的插件仓库列表。

    • 其他配置元素,如 <properties><activation> 等。

  • <activeProfiles>:激活一个或多个个人资料。通过指定个人资料的标识符来激活它们。

这里修改 <localRepository> 指定定本地仓库的目录,并增加阿里云为镜像仓库,以加快依赖包下载速度。同时修改 Maven 默认的 JDK 版本。

<?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>
  -->
  <localRepository>D:\.m2\repository</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>

  <!-- TODO Since when can proxies be selected as depicted? -->
  <!-- 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>aliyunmaven</id>
	  <mirrorOf>central</mirrorOf>
	  <name>alibaba mirror</name>
	  <url>https://maven.aliyun.com/repository/public</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 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>
    -->
	<profile>
      <id>development</id>
          <activation>
            <jdk>17</jdk>
            <activeByDefault>true</activeByDefault>
          </activation>
      <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>17</maven.compiler.source>
            <maven.compiler.target>17</maven.compiler.target>
      </properties>
	</profile>
      
    <!--
     | Here is another profile, activated by the 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>

项目

可以使用以下命令创建 Maven 项目:

mvn archetype:generate -DgroupId=net.stonecoding -DartifactId=java-project -DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.0-SNAPSHOT -DinteractiveMode=false

在使用 IDEA 创建 Maven 项目前,需要先修改 IDEA 的 Maven 配置,指定 Maven 程序位置,配置文件位置以及本地仓库位置:

image-20240526220855611

然后使用 IDEA 创建创建一个空的工程:

image-20240526222157734

然后创建 Maven 模块:

image-20240526223329237

创建完成后,会生成一个 pom.xml 文件:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>net.stonecoding</groupId>
  <artifactId>java01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>java01</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Maven 的 pom.xml 文件是项目的核心配置文件,它包含了项目的所有信息,如项目坐标、依赖关系、构建配置等。

坐标

Maven 中的坐标用于描述仓库中资源的位置。它由三个基本元素组成:groupIdartifactIdversion,以及在某些情况下还包括 packaging(打包方式)和 classifier(分类器)。

  • groupId

    • 通常表示项目所属的组织或公司的唯一标识符。例如,net.stonecoding
    • 它通常对应于 Java 的包结构,但并不要求完全一致。
  • artifactId

    • 定义了实际项目中的一个 Maven 项目(模块)。它通常是项目的名称或标识符。
    • 在构建时,Maven 会使用 artifactId 作为 JAR 文件(对于库)或目录(对于 Web 应用)的名称。
  • version

    • 定义了项目的当前版本。它通常是主版本号、次版本号和增量版本号的组合,例如 1.0.0
    • Maven 允许使用 SNAPSHOT 作为版本号的一部分,表示这是一个开发中的版本,可能会随时改变。
  • packaging(可选):

    • 表示项目的打包方式。Maven 支持多种打包方式,如 jar(Java 归档文件)、war(Web 应用程序归档文件)、ear(企业归档文件)等。如果省略,则默认为 jar
  • classifier(可选):

    • 用于帮助定义构建输出的一些附件或变体。例如,你可能想要生成一个包含源代码的 JAR 文件和一个仅包含文档的 JAR 文件。在这种情况下,你可以使用 classifier 来区分它们,如 sourcesjavadoc

例如上述 pom.xml 文件中:

  <groupId>net.stonecoding</groupId>
  <artifactId>java01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

依赖

在 Maven 中,依赖管理是一项非常重要的功能,它允许项目自动下载、解析和包含所需的库(也称为 JAR 文件)到项目中。这些库可以是 Java 标准库、第三方库或你自己的项目模块。Maven 通过在项目的 pom.xml 文件中声明依赖项来管理它们。

Maven 在 pom.xml 文件的 <dependencies> 部分使用一组坐标来唯一标识一个依赖项。例如:

<!--设置当前项目所依赖的所有 jar-->  
<dependencies>
  <!--设置具体的依赖-->
  <dependency>
    <!--依赖所属群组 id-->
    <groupId>junit</groupId>
    <!--依赖所属项目 id-->
    <artifactId>junit</artifactId>
    <!--依赖版本号-->
    <version>3.8.1</version>
    <!--依赖生效范围-->
    <scope>test</scope>
  </dependency>
</dependencies>

其中,<scope> 元素指定了这个依赖项的生效范围,包括:

  • compile(默认):在编译、测试和运行时都可用。例如 log4j
  • test:只在测试时可用。例如 junit
  • runtime:在测试和运行时可用,但在编译时不需要。例如 jdbc
  • provided:在编译和测试时可用,但在运行时由 JDK 或容器提供。例如 servlet-api
  • system:与 provided 类似,但需要显式地提供 JAR 文件的路径。
  • import:用于导入其他 pom.xml 文件中的依赖。它通常与 <dependencyManagement> 一起使用,来管理项目中多个模块的依赖版本。例如在 Spring Boot 项目中,可以使用 import 来引入 Spring Boot 的默认依赖管理配置,从而避免在多个模块中重复声明相同的依赖版本。

依赖传递

依赖传递允许 Maven 在处理项目依赖时自动将依赖项的依赖项也加入到当前项目的依赖列表中。即当一个项目 A 依赖于另一个项目 B 时,Maven 会自动将项目 B 的依赖项也加入到项目 A 的依赖列表中。这种依赖关系可以形成一个传递链,即项目 A 依赖于项目 B,项目 B 依赖于项目 C,那么项目 A 也间接依赖于项目 C。

当出现相同依赖时,根据以下规则来决定依赖的优先级:

  • 路径优先:当依赖中出现相同的资源时,层级越深,优先级越低;层级越浅,优先级越高。
  • 声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的。
  • 特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的。

有时可能不希望包含某个传递性依赖项,可以使用 <exclusions> 元素来排除这些依赖项。例如:

<dependency>  
    <groupId>example.com</groupId>  
    <artifactId>project-a</artifactId>  
    <version>1.0.0</version>  
    <exclusions>  
        <exclusion>  
            <groupId>example.com</groupId>  
            <artifactId>unwanted-library</artifactId>  
        </exclusion>  
    </exclusions>  
</dependency>

可选依赖

可选依赖允许库(即 Maven 依赖)声明其自己的某些依赖是可选的。这意味着,当其他项目包含这个库作为依赖时,这些可选的依赖不会被自动包含进来,除非它们显式地请求这些依赖。

使用可选依赖可以:

  • 避免依赖冲突:有时,一个库可能依赖于另一个库的特定版本,但使用你的库的项目可能已经包含了该库的另一个版本。通过将依赖标记为可选,你的库可以避免与其他依赖版本发生冲突。
  • 减少传递性依赖:有些依赖可能只在库的某些特定功能中使用,而不是整个库都需要。通过将它们标记为可选,你可以减少其他项目在包含你的库时所需的额外依赖。

在 Maven 的 pom.xml 文件中,可以使用 <optional> 标签来声明一个可选依赖。例如:

<dependencies>  
    <!-- 其他非可选依赖 ... -->  
  
    <dependency>  
        <groupId>com.example</groupId>  
        <artifactId>some-library</artifactId>  
        <version>1.0.0</version>  
        <optional>true</optional>  
    </dependency>  
  
    <!-- 其他非可选依赖 ... -->  
</dependencies>

假设 A 依赖 B,B 依赖 C,C 通过依赖传递会被 A 使用到,现在要想办法让 A 不去依赖 C,可以:

  • 可选依赖:在 B 上设置 <optional>,A 不知道有 C 的存在。
  • 排除依赖:在 A 上设置 <exclusions>,A 知道有 C 的存在,主动将其排除掉。

聚合

聚合允许通过一个父项目(<packaging>pom)来构建多个模块。聚合项目本身不包含代码,而是用于管理和构建一组相关的 Maven 项目模块。在聚合项目的 pom.xml 文件中,会使用 <modules> 元素列出所有需要被聚合的子模块。

例如:

<project>  
    <modelVersion>4.0.0</modelVersion>  
  
    <groupId>com.example</groupId>  
    <artifactId>parent-project</artifactId>  
    <version>1.0.0</version>  
    <packaging>pom</packaging>  
  
    <name>Parent Project</name>  
    <url>http://maven.apache.org</url>  
  
    <modules>  
        <module>module-a</module>  
        <module>module-b</module>  
        <!-- 更多的子模块 -->  
    </modules>  
  
    <!-- 依赖管理和其他配置可以放在这里,但不会影响子模块的构建 -->  
  
</project>

注意:

  • 聚合项目本身不构建代码:聚合项目只是一个容器,它不包含任何源代码或资源,只是用来定义一组模块的集合。
  • 模块路径<modules> 元素中的子模块名称是相对于聚合项目的根目录的。如果有一个子目录结构(如 module-a/pom.xml),只需要在 <modules> 中指定 module-a 而不是 module-a/pom.xml
  • 构建顺序:Maven 默认按照 <modules> 列表中声明的顺序构建模块。但是,如果模块之间存在依赖关系,Maven 会自动解析这些依赖关系,并按照正确的顺序构建模块。
  • 依赖管理:虽然可以在聚合项目的 pom.xml 文件中使用 <dependencyManagement> 来管理依赖的版本,但这并不会自动将这些依赖添加到子模块的类路径中。子模块仍然需要在自己的 pom.xml 文件中声明这些依赖。<dependencyManagement> 只是提供了一种方式来定义依赖的版本,以便在整个项目中保持一致。
  • 插件配置:聚合项目的 pom.xml 文件中可以定义插件配置,但这些配置不会直接应用于子模块。如果需要在所有子模块中共享某些插件配置,可以考虑使用 Maven 的父 POM(Inheritance)功能。
  • 执行构建:当在聚合项目的根目录下运行 mvn clean install 时,Maven 会自动构建所有列在 <modules> 中的子模块。可以使用 Maven 的命令行选项来指定要构建的模块,如 mvn -pl module-a clean install 只会构建 module-a

通过 Maven 的聚合功能,可以更容易地管理一组相关的 Maven 项目模块,并在一个命令中构建整个项目。

继承

继承允许一个Maven项目(称为子项目)从另一个Maven项目(称为父项目)中继承配置信息。这种机制使得在多个项目中共享同一配置信息变得容易,从而简化了项目的管理和维护工作。

具体作用:

  • 统一管理依赖信息:在大型项目中,经常需要对项目进行模块拆分。每个模块都需要配置自己的依赖信息,而这些依赖信息往往是相同的或相似的。通过继承,可以将这些共同的依赖信息提取到父项目中,从而减少重复配置。
  • 统一项目规范:父项目可以定义一些公共的属性、插件配置等,这些配置将被子项目继承。这样可以确保整个项目遵循统一的规范和标准。
  • 简化项目维护:当需要修改某个公共配置时,只需要在父项目中修改一次即可,无需在每个子项目中都进行修改。

实现步骤:

  • 定义父项目:
    • 父项目的 pom.xml 文件中,<packaging> 标签的值应该设置为 pom,表示这是一个聚合项目或父项目。
    • 在父项目的 pom.xml 文件中,可以定义公共的 <properties><dependencyManagement><build> 等元素。
  • 子项目继承父项目:
    • 在子项目的 pom.xml 文件中,使用 <parent> 元素指定父项目的坐标(包括 groupIdartifactIdversion)。
    • 子项目将自动继承父项目中定义的属性、依赖、插件配置等。

注意:

  • 版本冲突:如果子项目在继承了父项目的依赖后,又自己定义了相同依赖的不同版本,那么将会以子项目中定义的版本为准。这可能会导致版本冲突问题,因此需要注意依赖的版本管理。
  • 插件配置:虽然子项目可以继承父项目的插件配置,但某些插件配置可能需要在子项目中进行覆盖或补充。因此,在配置插件时需要注意这一点。
  • 子项目之间的依赖关系:在 Maven 项目中,子项目之间也可能存在依赖关系。这些依赖关系需要在各自的 pom.xml 文件中进行定义,而不是通过继承来实现。

假设有一个父项目 parent-project,它定义了一个公共的依赖 spring-boot-starter-web

<!-- parent-project/pom.xml -->  
<project>  
    ...  
    <groupId>com.example</groupId>  
    <artifactId>parent-project</artifactId>  
    <version>1.0.0</version>  
    <packaging>pom</packaging>  
    ...
    
    <!--定义依赖,抽取子项目中公有的 JAR 包-->
    <dependencies>
    	<dependency>
      		<groupId>junit</groupId>
      		<artifactId>junit</artifactId>
      		<version>4.12</version>
      		<scope>test</scope>
    	</dependency>
    <dependencies>
    
    <!--定义可选依赖-->
    <dependencyManagement>  
        <dependencies>  
            <dependency>  
                <groupId>org.springframework.boot</groupId>  
                <artifactId>spring-boot-starter-web</artifactId>  
                <version>2.6.3</version>  
            </dependency>  
        </dependencies>  
    </dependencyManagement>  
    ...  
</project>

其中 <dependencyManagement> 标签不真正引入 JAR 包,只是管理 JAR 包的版本。

然后有一个子项目 child-project,它继承了 parent-project

<!-- child-project/pom.xml -->  
<project>  
    ...
    <!--定义该工程的父工程-->
    <parent>  
        <groupId>com.example</groupId>  
        <artifactId>parent-project</artifactId>  
        <version>1.0.0</version>  
    </parent>  
    ...  
    <dependencies>  
        <!-- 使用父工程中可选依赖的坐标,无需指定版本 -->  
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-web</artifactId>  
        </dependency>  
        ...  
    </dependencies>  
    ...  
</project>

在这个例子中,child-project 继承了 parent-project 中定义的 spring-boot-starter-web 依赖,并直接引用而无需指定版本。当父项目 <dependencyManagement> 标签中 JAR 包版本发生变化,所有子项目中有用到该 JAR 包的地方对应的版本会自动随之更新。

聚合与继承的作用:

  • 聚合用于快速构建项目,对项目进行管理。
  • 继承用于快速配置和管理子项目中所使用 JAR 包的版本。

聚合和继承的相同点:

  • 聚合与继承的 pom.xml 文件打包方式均为 pom,可以将两种关系制作到同一个 pom 文件中。
  • 聚合与继承均属于设计型模块,并无实际的模块内容。

聚合和继承的不同点:

  • 聚合是在当前模块中配置关系,可以感知到参与聚合的模块有哪些。
  • 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己。

属性

在 Maven 中,可以通过 <properties> 元素在项目的 POM 文件中定义自定义属性。这些自定义属性可以在 POM 文件的其他部分中被引用,为构建配置提供灵活性。最常用于指定 JAR 包的版本。

例如在工程中定义属性:

<properties>
    <spring.version>5.2.10.RELEASE</spring.version>
    <junit.version>4.12</junit.version>
    <mybatis-spring.version>1.3.0</mybatis-spring.version>
</properties>

就可以在依赖中使用这些属性,以便后续统一调整版本:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>

除了自定义属性外,Maven 还有内置属性,包括:

  • ${basedir}:项目的根目录。
  • ${version}:项目的版本。
  • ${project.build.directory}:构建目录,通常是 target/
  • ${project.build.outputDirectory}:构建的输出目录,通常是 target/classes
  • ${project.artifactId}${project.groupId}${project.version}:分别代表项目的 artifactIdgroupIdversion

还有系统属性和环境变量属性,可以通过命令 mvn help:system 查看。

生命周期

Maven 生命周期是 Maven 构建过程中的一系列有序阶段,每个阶段代表构建过程的一个步骤。Maven 生命周期是抽象的,这意味着实际上不执行任何任务,但它为 Maven 插件的执行定义了一种顺序和逻辑。Maven 生命周期包括三个主要的内置生命周期:default(默认),cleansite

  • default 生命周期处理项目的部署,它包含以下阶段:

    • validate:验证项目是否正确且所有必要信息都可用。

    • initialize:初始化构建状态,例如设置属性或创建目录。

    • generate-sources:生成任何源代码。

    • process-sources:处理源代码,例如过滤一些值。

    • generate-resources:生成资源文件。

    • process-resources:复制和处理资源文件到目标目录,准备打包。

    • compile:编译项目的源代码。

    • process-classes:后处理编译生成的文件,例如对Java类进行字节码增强。

    • generate-test-sources:生成测试源代码。

    • process-test-sources:处理测试源代码。

    • generate-test-resources:生成测试资源文件。

    • process-test-resources:复制和处理测试资源文件到测试目标目录。

    • test-compile:编译测试源代码。

    • process-test-classes:后处理编译的测试源文件。

    • test:运行测试。

    • prepare-package:执行实际打包之前的准备步骤。

    • package:将编译的代码打包成可分发的格式,如 JAR、WAR 等。

    • pre-integration-test:在执行集成测试之前执行所需的操作。

    • integration-test:执行集成测试。

    • post-integration-test:执行集成测试后执行所需的操作。

    • verify:检查包是否有效且符合质量标准。

    • install:将包安装到本地仓库,以便其他项目可以使用这个包。

    • deploy:将最终的包复制到远程仓库,以便与其他开发者和项目共享。

  • clean 生命周期的目的是清理之前构建生成的文件,它只有一个阶段:

    • clean:删除 target/ 目录,该目录包含了构建生成的所有文件。
  • site 生命周期的目的是生成项目的站点文档,它包含以下阶段:

    • pre-site:执行一些在生成站点文档之前需要完成的工作。

    • site:生成项目的站点文档。

    • post-site:执行一些在生成站点文档之后需要完成的工作,如站点部署。

    • site-deploy:将生成的站点文档部署到特定的服务器上。

这些生命周期中的每个阶段都可以绑定一个或多个 Maven 插件目标(Goals),这些插件会在相应的阶段执行。开发者可以通过在项目的 pom.xml 文件中配置插件来定制 Maven 构建过程。

可以通过命令行工具执行特定的生命周期阶段。Maven 生命周期的各个阶段是有顺序的,并且如果执行一个阶段,Maven 会自动执行该阶段之前所有未执行的阶段。

以下是一些常用的 Maven 生命周期阶段执行命令:

  • 编译项目:执行 compile 阶段,这会编译项目的源代码。

    mvn compile
    
  • 测试项目:执行 test 阶段,这会编译并运行项目中的单元测试。

    mvn test
    
  • 打包项目:执行 package 阶段,这会将项目打包成 JAR、WAR 或 EAR 文件,取决于项目的打包方式。

    mvn package
    
  • 安装项目到本地仓库:执行 install 阶段,这会将打包好的文件安装到本地 Maven 仓库中,以便其他项目可以引用。

    mvn install
    
    #将本地的 JAR包上传到本地Maven仓库
    mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=jar
    
  • 部署项目到远程仓库:执行 deploy 阶段,这会将项目部署到远程 Maven 仓库(需要配置仓库信息)。

    mvn deploy
    
  • 清理构建文件:执行 clean 阶段,这会删除 target 目录及其内容,即清理上一次构建生成的文件。

    mvn clean
    
  • 组合命令:可以组合多个阶段一起执行,例如先清理再打包:

    mvn clean package
    

    或者先清理、编译、测试再打包:

    mvn clean compile test package
    
  • 生成站点:执行 site 阶段,生成项目的站点文档。

    mvn site
    
  • 跳过单元测试:如果想在执行某个阶段时跳过单元测试,可以使用 -DskipTests 参数:

    mvn package -DskipTests
    

请注意,Maven 命令需要在包含 pom.xml 文件的目录下执行,因为这个文件描述了项目的构建配置和依赖关系。此外,Maven 的行为还可以通过命令行参数、环境变量或 settings.xml 配置文件进行进一步的定制。

私服

Maven 私服,也称为 Maven 私有仓库,由组织自行搭建和维护,通常部署在企业内部网络环境中。它允许团队在本地搭建一个中央存储库,用于保存和分享项目构建所需的依赖项。

使用 Maven 私服可以:

  • 节省外网带宽:大量对于外部远程仓库的重复请求会消耗大量的带宽,利用 Maven 私服代理外部仓库后,能够减少对外部仓库的重复请求,从而降低外网带宽压力。
  • 下载速度更快:Maven 私服位于局域网内,因此从私服下载构件的速度更快、更稳定。
  • 便于部署第三方构件:有些构件无法从任何一个远程仓库中获得(例如,某公司或组织内部的私有构件、Oracle 的 JDBC 驱动等),建立私服之后,可以将这些构件部署到私服中,供内部 Maven 项目使用。
  • 提高项目的稳定性:如果不建立私服,Maven 项目的构件就高度依赖外部的远程仓库。若外部网络不稳定,项目的构建过程也会变得不稳定。建立私服后,即使外部网络状况不佳,也能保证项目的稳定构建。

常见的 Maven 私服产品包括 Nexus、Artifactory 和 Apache Archiva。

部署

这里使用 Nexus 部署 Maven 私服。

推荐配置如下:

Profile SizeProfile DescriptionCPUsDisk Size (Blob)RAM
Small< 20 repositories
< 20GB total blob store size
single repository format type
820GB8GB
Medium< 50 repositories
< 200GB total blob store size
a few repository formats
8+200GB16GB
Large< 200 repositories
> 200GB total blob store size
diverse repository formats
Note: Deployments of this scale should use a PostgreSQL database
12+200GB or more32GB
Very Large200+ repositories
~10TB total blob store size
diverse repository formats
Note: Deployments of this scale should use a PostgreSQL database
16+10TB or more64GB

配置 Linux 操作系统环境:

[root@stone ~]# cat /etc/redhat-release 
CentOS Linux release 7.9.2009 (Core)
[root@stone ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.92.131   stone
[root@stone ~]# systemctl stop firewalld
[root@stone ~]# systemctl disable firewalld
[root@stone ~]# vi /etc/security/limits.conf
[root@stone ~]# tail -2 /etc/security/limits.conf
* hard nofile 65536
* soft nofile 65536

需要先下载open in new window安装 Java 8:

[root@stone ~]# tar -xvzf jdk-8u201-linux-x64.tar.gz
[root@stone ~]# vi .bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin

export PATH

export JAVA_HOME=/root/jdk1.8.0_201
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

[root@stone ~]# source .bash_profile
[root@stone ~]# java -version
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

官方网站open in new window下载对应操作系统的安装程序,再解压安装程序:

[root@stone ~]# tar -xvzf nexus-3.68.1-02-java8-unix.tar.gz

启动 Nexus:

[root@stone ~]# cd nexus-3.68.1-02/bin/
[root@stone bin]# ./nexus start

然后访问 http://192.168.92.131:8081/:open in new window

image-20240528223311602

点击 “Sign in”,提示用户名为 admin 的密码在文件 /root/sonatype-work/nexus3/admin.password 中:

image-20240528223405701

获取密码登录后,重置密码:

image-20240528223622423

启用匿名访问:

image-20240528224435413

至此私服就已经安装成功。如果要想修改一些基础配置信息,可以:

  • 修改基础配置信息
    • 安装路径下 etc 目录中 nexus-default.properties 文件保存有 Nexus 基础配置信息,例如默认访问端口。
  • 修改服务器运行配置信息
    • 安装路径下 bin 目录中 nexus.vmoptions 文件保存有 Nexus 服务器启动对应的配置信息,例如默认占用内存空间。

配置

在企业多人开发中,一般有如下需求:

  • 将开发的程序安装到本地仓库并上传到私服仓库中,以供其他人员使用。
  • 其他人可以从私服仓库中获取上传的程序包。
  • 当需要从远程中央仓库下载第三方程序包,可以由私服进行代理下载,存储在私服专用仓库中,下次再访问就直接走私服下载。
  • 将不同版本的程序包放入到不同的仓库。
  • 为方便获取,将多个仓库编成一个组,只需要访问仓库组就可以获取所有资源。

可将私服仓库分为三大类:

  • 宿主仓库(hosted):

    • 保存无法从中央仓库获取的资源

    • 自主研发

    • 第三方非开源项目,比如 Oracle 的 JDBC 驱动,因为是付费产品,所以中央仓库没有

  • 代理仓库(proxy):

    • 代理远程仓库,通过 Nexus 访问其他公共仓库,例如中央仓库
  • 仓库组(group):

    • 将若干个仓库组成一个群组,简化配置

    • 仓库组不能保存资源,属于设计型仓库

仓库类别英文名称功能关联操作
宿主仓库hosted存放自主开放及第三方程序包上传
代理仓库proxy代理连接中央仓库下载
仓库组group为仓库编组简化下载操作下载

在 Nexus 中创建 2 个宿主仓库,分别用于存放 SNAPSHOT 和 RELEASE 版本程序包:

image-20240530213757597

类别选择 “maven2(hosted)”:

image-20240531205301089

创建 SNAPSHOT 仓库:

image-20240531211017650

然后再创建 RELEASE 仓库:

image-20240531211135671

将上面创建的仓库加入到 “maven-public” 仓库组中:

image-20240531211305741

根据上面的需求,在本地 Maven 的配置文件 settings.xml 中进行如下配置:

  • 配置本地仓库对私服的访问权限:
<servers>
    <server>
        <id>stonecoding-snapshot</id>
        <username>admin</username>
        <password>admin</password>
    </server>
    <server>
        <id>stonecoding-release</id>
        <username>admin</username>
        <password>admin</password>
    </server>
</servers>
  • 配置私服仓库组的访问路径,以便从此仓库组进行下载:
<mirrors>
    <mirror>
        <!--配置仓库组的 ID-->
        <id>maven-public</id>
        <!--* 表示所有内容都从私服获取-->
        <mirrorOf>*</mirrorOf>
        <!--私服仓库组 maven-public 的访问路径-->
        <url>http://192.168.92.131:8081/repository/maven-public/</url>
    </mirror>
</mirrors>

在项目的 pom.xml 中配置当前工程保存在私服中的具体位置,以便上传程序包到私服:

    <distributionManagement>
        <repository>
            <!--和 maven/settings.xml 中 server 中的 id 一致,表示使用该 id 对应的用户名和密码-->
            <id>stonecoding-release</id>
            <!--release 版本上传仓库的具体地址-->
            <url>http://192.168.92.131:8081/repository/stonecoding-release/</url>
        </repository>
        <snapshotRepository>
            <!--和 maven/settings.xml 中 server 中的 id 一致,表示使用该 id 对应的用户名和密码-->
            <id>stonecoding-snapshot</id>
            <!--snapshot 版本上传仓库的具体地址-->
            <url>http://192.168.92.131:8081/repository/stonecoding-snapshot/</url>
        </snapshotRepository>
    </distributionManagement>

注意:要发布的项目都需要配置 distributionManagement 标签,要么在自己的 pom.xml 中配置,要么在其父项目中配置,然后子项目中继承父项目即可。

然后在 IDEA 中发布资源到私服:

image-20240531214854947

或者执行 Maven 命令:

mvn deploy

由于在 pom.xml 中指定了 version1.0-SNAPSHOT,故会发布到私服的 stonecoding-snapshot 仓库中:

image-20240531215753145

如果想发布到 stonecoding-release 仓库中,则需要将 pom.xml 中的 version 修改为 1.0-RELEASE 即可。

如果私服中没有对应的程序包,会去中央仓库下载,速度很慢。可以修改 maven-central 仓库,将 “Remote storage“ 的地址由 https://repo1.maven.org/maven2/open in new window 修改为 https://maven.aliyun.com/repository/publicopen in new window 并保存,让私服从阿里云下载依赖:

image-20240531221200356

上次编辑于:
贡献者: stone,stonebox