Maven

作者: 刚子来简书啦 | 来源:发表于2020-09-27 11:30 被阅读0次

Maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。作为Apache组织中的一个颇为成功的开源项目,Maven主要服务于基于Java平台的项目的构建、依赖管理和项目信息管理。

Maven作为一个构建工具,不仅帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的接口操作,这一切足以使它成为优秀的、流行的构建工具。

下载地址:http://maven.apache.org/download.cgi
设置PATH环境变量
export M2_HOME=~/bin/apache-maven-3.6.3
export PATH=$PATH:$M2_HOME/bin

Maven项目的核心是pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>com.cage.study.spring</groupId>
    <artifactId>spring-study</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>Spring Study Project</name>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

以上是一个最基本的包含了主代码和单元测试的pom.xml。

# 编译
mvn clean compile
# 测试
mvn clean test
# 打包
mvn clean package
# 发布到库
mvn clean install

Maven有一些约定:在项目的根目录中放置pom.xml,在src/main/java目录中放置项目的主代码,在src/test/java中放置项目的测试代码。我们将这些基本的目录结构和pom.xml文件内容称为项目的骨架。Maven提供了archetype帮助我们快速勾勒出项目骨架。

mvn archetype:generate

Maven坐标

为了能自动化地解析任何一个Java构件,Maven就必须将它们唯一标识,这就依赖管理的底层基础——坐标。Maven坐标的元素包括:groupId、artifactId、version、packaging、classifier。在我们开发自己项目的时候,也需要为其定义适当的坐标,这是Maven强制要求的。

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

根元素project下的dependencies可以包含一个或多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:

  • groupId、artifactId和version:依赖的基本坐标。
  • type:依赖的类型,默认为jar。
  • sope:依赖的范围。
  • optional:标记依赖是否可选。
  • exclusions:用来排除传递性依赖。

Maven在编译项目主代码的时候需要使用一套classpath;在编译和执行测试的时候会使用另外一套classpath;实际运行Maven项目的时候,又会使用一套classpath。依赖范围是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系的,Maven有以下几种依赖范围:

  • compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。
  • test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或运行项目的使用时将无法使用此类依赖。
  • provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath都有效,但在运行时无效。
  • runtime:运行时依赖范围。使用此范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。
  • system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由此此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。systemPath元素可以引用环境变量,如 ${java.home}。
  • import:导入依赖范围。该依赖范围不会对三种classpath产生实际影响。

仓库

在Maven世界中,任何一个依赖、插件或项目构建的输出,都可以称为构件。任何一个构件都有一组坐标唯一标识。得益于坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。实际的Maven项目不再各自存储其依赖文件,它们只需要声明这些依赖的坐标,在需要的时候,Maven会自动根据坐标找到仓库中的构件,并使用它们。

任何构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这便是Maven的仓库布局方式。例如,log4j:log4j:1.2.15这一依赖,其对象的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar。

https://mvnrepository.com/

对于Maven来说,仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在,则直接使用;如果本地仓库没有,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件后下载到本地仓库再使用。

中央仓库是Maven核心自带的远程仓库,它包含了绝大部分开源的构件。在默认配置下,当本地仓库没有Maven需要的构件的时候,它就会尝试从中央仓库下载。私服是一种特殊的远程仓库,为了节省带宽和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有外部的远程仓库。

Maven默认的本地仓库地址是 ~/.m2/repository,在这里可以看到所有下载的构件。可以通过修改settings配置文件,添加localRepository属性来自定义本地仓库目录。建议将Maven安装目录里conf下的settings.xml拷贝到~/.m2目录下进行维护。

<settings>
    <localRepository>/path/to/local/repo</localRepository>
</settings>

Maven的安装文件中自带了中央仓库的地址,所以空配置的情况下依然可以找到中央仓库。以下是从安装目录的lib里maven-model-builder.jar中解压的配置文件,包含了中央仓库的地址。

  <repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
    </pluginRepository>
  </pluginRepositories>

生命周期和插件

除了坐标、依赖和仓库外,Maven两外两个核心概念是生命周期和插件。在Maven的日常使用中,生命周期的输入往往就对应了生命周期,如 mvn package 就表示执行默认生命周期阶段 package。Maven的生命周期是抽象的,其实际行为都由插件来完成,如 package 阶段的任务可能就会由 maven-jar-plugin 完成。生命周期和插件协同工作,两者密不可分。

Maven的生命周期就是为了对所有的构建过程进行抽象和统一。Maven从大量项目和构件工具中学习和反思,然后总结了一套高度完善的、易扩展的生命周期。这个生命周期包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。也就是说,几乎所有项目的构建,都能映射到这样一个生命周期上。

Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在Maven的设计中,实际的任务都交由插件来完成。每个构建步骤都可以绑定一个或者多个插件行为,而且Maven为大多数构建步骤编写并绑定了默认插件。

Maven拥有三套相互独立的生命周期,它们分别为clean、default和site。clean的目的是清理项目,default的目的是构建项目,site的目的是建立项目站点。每个生命周期包含一些阶段(phase),这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段。

clean生命周期

  • preclean
  • clean
  • post-clean

default生命周期

  • validate
  • initialize
  • generate-sources
  • process-sources 处理项目主资源文件。
  • generate-resources
  • process-resources
  • compile 编译项目的主代码。
  • process-classes
  • generate-test-sources
  • process-test-sources 处理项目测试资源文件。
  • generate-test-resources
  • process-test-resources
  • test-compile 编译项目的测试代码。
  • process-test-classes
  • test 使用单元测试框架运行测试,测试代码不会被打包或部署。
  • prepare-package
  • package 将编译好的代码,打包成可发布的格式,如JAR。
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install 将包安装到本地仓库,供其他Maven项目使用。
  • deploy 将包安装到远程仓库,供其他开发人员和Maven项目使用。

site生命周期

  • pre-site
  • site 生成项目站点文档。
  • post-site
  • site-deploy 将生成的项目站点发布到服务器上。

从命令行执行Maven任务的最主要方式就是调用Maven的生命周期阶段。需要注意的是,各个生命周期是相互对立的,而一个生命周期的阶段是有前后依赖关系的。

一个插件可以完成一个或多个功能,每个功能就是一个插件目标。比如 maven-dependency-plugin 有十多个目标,每个目标对应了一个功能:dependency:analyze、dependency:tree和dependency:list等。这是一种通用的写法,冒号前面是插件前缀,后面是该插件的目标。

Maven的生命周期与插件相互绑定,用以完成实际的构建任务。具体而言,是生命周期的阶段与插件的目标相互绑定,以完成某个具体的构建任务。为了能让用户几乎不用任何配置就能构建Maven项目,Maven在核心为一些主要的生命周期阶段绑定了很多插件的目标,当用户通过命令行调用生命周期阶段的时候,对应的插件目标就会执行相应的任务。

clean生命周期阶段与插件目标的绑定关系

生命周期阶段 插件目标
clean maven-clean-plugin:clean

default生命周期阶段与插件目标的绑定关系

生命周期阶段 插件目标
process-resources maven-resources-plugin:resources
compile maven-compiler-plugin:compile
process-test-resources maven-resources-plugin:testResources
test-compile maven-compiler-pugin:testCompile
test maven-surefire-plugin:test
package maven-jar-plugin:jar
install maven-install-plugin:install
deploy maven-deploy-plugin:deploy

site生命周期阶段与插件目标的绑定关系

生命周期阶段 插件目标
site maven-site-plugin:site
site-deploy maven-site-plugin:deploy

下面是自定义一个绑定关系,将项目打包成可执行的jar包,需要使用shade插件。

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <manifestEntries>
                                        <Main-Class>com.cage.study.App</Main-Class>
                                    </manifestEntries>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

配置信息参考 http://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ManifestResourceTransformer

聚合与继承

为了能够使用一条命令就能构建多个模块,我们可以额外添加一个Maven项目,然后使用聚合的POM配置来进行整合。

    <groupId>com.cage.study.spring</groupId>
    <artifactId>spring-study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>Wallet</module>
        <module>Ticker</module>
    </modules>
    <name>Spring Study Project</name>

对于聚合项目来说,其打包方式packaging的值必须为pom,否则就无法构建。聚合项目本身不做任何功能实现,往往建在模块的最顶层。

聚合项目也可以建在平行目录,但是module的引用需要使用POM文件的相对地址。
<module>../Wallet</module>
<module>../Ticker</module>

为了解决多个模块重复配置相同属性的问题,我们可以额外添加一个Maven项目,然后让其他模块都继承它,以实现一处配置,多出使用的效果。

模块的平行目录建立一个空项目,配置POM与聚合项目类似。

    <groupId>com.cage.study.spring</groupId>
    <artifactId>pom-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

其他模块继承此模块的配置时,POM添加如下配置:

    <parent>
        <artifactId>pom-parent</artifactId>
        <groupId>com.cage.study.spring</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom-parent/pom.xml</relativePath>
    </parent>

parent标签即表示要继承的模块,此时需要添加relativePath来引用到被继承模块的POM配置文件。可以被继承的POM元素如下:

  • groupId:项目组ID,项目坐标的核心元素。
  • version:项目版本,项目坐标的核心元素。
  • description:项目的描述信息。
  • organization:项目的组织信息。
  • inceptionYear:项目的创始年份。
  • url:项目的URL地址。
  • developers:项目的开发者信息。
  • contributors:项目的贡献者信息。
  • distributionManagement:项目的部署配置。
  • issueManagement:项目的缺陷跟踪系统信息。
  • ciManagement:项目的持续集成系统信息。
  • scm:项目的版本控制系统信息。
  • mailingLists:项目的邮件列表信息。
  • properties:自定义的Maven属性。
  • dependencies:项目的依赖配置。
  • dependencyManagement:项目的依赖管理配置。
  • repositories:项目的仓库配置。
  • build:包括项目的源码目录配置、输出目录位置、插件配置、插件管理配置等。
  • reporting:包括项目的报告输出目录配置、报告插件配置等。

属性

Maven有六类属性,分别为:

  • 内置属性:主要有两个常用的内置属性,${basedir}表示项目根目录,即包含pom.xml文件的目录,${version}表示项目版本。
  • POM属性:用户可以使用此类属性引用POM文件中对应元素的值。例如${project.artifactId}就对应了<project><artifactId>元素的值。
  • 自定义属性:用户可以在POM的<properties>元素下自定义Maven属性,然后直接通过变量名进行引用。
  • Settings属性:与POM属性同理,用户使用以 settings. 开头的属性引用 settings.xml 文件中的XML元素的值,如 ${settings.localRepository} 指向用户本地仓库的地址。
  • Java系统属性:所有Java系统属性都可以使用Maven属性引用,如 ${user.home} 指向了用户目录。用户可以使用 mvn help:system 查看所有的Java系统属性。
  • 环境变量属性:所有环境变量都可以使用以 env. 开头的Maven属性引用,如 ${env.JAVA_HOME} 指代了JAVA_HOME环境变量的值。用户可以使用 mvn help:system 查看所有的环境变量。

相关文章

网友评论

      本文标题:Maven

      本文链接:https://www.haomeiwen.com/subject/qvsaphtx.html