Maven 基础


Maven 介绍

什么是 Maven ?

在学习 Maven 之前,我们先来看一下,没使用 Maven 构建的项目都会有哪些问题。

假设你现在做了一个 CRM 的系统,项目中肯定要用到一些 jar 包,比如说 mybatis、log4j、JUnit 等。除了这些之外,还有可能用到我们同事开发的其他的东西,比如说别人做了一个财务模块或做了一个结算的模块,你在这里边有可能要用到这些东西。

image

假设有一天,我们项目中的 mybatis 进行了一个升级,但是它内部使用的 JUnit 没有升级。升级以后的 mybatis 假如要用 JUnit5,而项目中目前用的是 4.0 的,会不会冲突?必然会出问题!这个时候管理起来会比较麻烦,你需要各种调整。更有甚者,假如同事做的这些东西升级了但又没有通知你,这个时候,就会出现几种严重的问题:

  1. jar 包不统一、jar 包不兼容
  2. 工程升级维护过程操作繁琐
  3. 除此之外,还会有其它的一系列问题。

那么要想解决这些问题,就可以用到 Maven 了。

Maven 的本质是一个项目管理工具,将项目开发和管理过程抽象成一个项目对象模型(POM)

Maven 是用 Java 语言编写的。它管理的东西统统以面向对象的形式进行设计,最终他把一个项目看成一个对象,而这个对象叫做 POM(project object model),即项目对象模型。

image

既然一个项目就是一个对象,那么作为对象的行为和属性都有哪些呢?

Maven 说我们需要编写一个 pom.xml 文件,Maven 通过加载这个配置文件就可以知道我们项目的相关信息了。因此,Maven 离不开一个叫 pom.xml 的文件,因为这个文件代表就一个项目。

思考:如果我们做 8 个项目,那么对应的是 1 个还是 8 个文件?
答:8 个

那 Maven 是如何帮我们进行项目资源管理的呢?这就需要用到 Maven 中的第二个东西:依赖管理,这也是它的第二个核心。

所谓依赖管理就是指 Maven 对项目所有依赖资源的一种管理,它和项目之间是一种双向关系,即当我们做项目的时候,Maven 的依赖管理可以帮助你去管理你所需要的其他资源;当其他的项目需要依赖我们项目的时候,Maven 也会把我们的项目当作一种资源去进行管理。这就是一种双向关系。

那 Maven 的依赖管理的资源存在哪儿呢?主要有三个位置:本地仓库、私服、中央仓库

  • 本地仓库顾名思义就是存储在本地的一种资源仓库。
  • 如果本地仓库中没有相关资源,可以去私服上获取,私服也是一个资源仓库,只不过不在本地,是一种远程仓库。
  • 如果私服上也没有相关资源,可以去中央仓库去获取,中央仓库也是一种远程仓库。

Maven 除了帮我们管理项目资源之外,还能帮助我们对项目进行构建,管理项目的整个生命周期。当然它的这些功能需要使用一些相关的插件来完成,并且整个生命周期过程中,插件是需要配合使用的,单独一个插件无法完成完整的生命周期。


Maven 作用及结构

Maven 的作用可以总结成三点:

  1. 项目构建:提供标准的、跨平台的自动化构建项目的方式。
  2. 依赖管理:方便快捷地管理项目依赖的资源(jar 包),避免资源间的版本冲突等问题。
  3. 统一开发结构:提供标准的、统一的项目开发结构。如下图所示:
    image

各目录存放资源说明:

  • src/main/java:项目 Java 源码。
  • src/main/resources:项目的相关配置文件(比如 Mybatis 配置、XML 映射配置、自定义配置文件等)。
  • src/main/webapp:Web 资源(比如 HTML、CSS、JS 等)。
  • src/test/java:测试代码。
  • src/test/resources:测试相关配置文件。
  • src/pom.xml:项目 pom 文件。

Maven 环境搭建

maven 官网:http://maven.apache.org/

1)登录官网下载解压版本

进入官方下载页面,如下所示:

image

最新版本:

image

其他版本:

image

2)解压缩

3)配置环境变量 MAVEN_HOME

  • 注意:变量名必须是 MAVEN_HOME

4)验证安装成功

启动命令行,输入:mvn --version

image

5)IDEA 配置 Maven:

image


Maven 核心概念

仓库

仓库用于存储资源,主要是各种 jar 包。

image

  • 中央仓库:Maven 团队自身维护的仓库,属于开源的。

  • 私服:各公司/部门等小范围内存储资源的仓库,私服也可以从中央仓库获取资源。

  • 本地仓库:开发者自己电脑上存储资源的仓库,也可从远程仓库获取资源。

私服的作用:

  1. 保存具有版权的资源,比如购买或自主研发的 jar 。
  2. 一定范围内共享资源,能做到仅对内而不对外开放。

坐标

Maven 的仓库里存储了各种各样的资源(jar 包),那我们是如何找到这些资源的呢?为此,我们需要知道它们具体的一个位置才能知道如何找到它们,这个就叫坐标。

坐标:使用唯一标识描述仓库中资源的位置,唯一性地定义资源位置。通过该标识可以将资源的识别与下载工作交由机器完成。

https://repo1.maven.org/maven2/

那 Maven 中的坐标是如何构成的呢?主要组成如下:

  • groupId:定义当前资源隶属组织名称(通常是域名反写,如:org.mybatis、com.baidu)。

  • artifactId:定义当前资源的名称(通常是项目或模块名称,如:crm,sms)。

  • version:定义当前资源的版本号。

packaging:定义资源的打包方式,取值一般有如下三种:

  1. jar:该资源打成 jar 包(默认配置)
  2. war:该资源打成 war 包
  3. pom:该资源是一个父资源(表明使用 maven 分模块管理),打包时只生成一个 pom.xml,而不生成 jar 或其他包结构。

如果要查询 Maven 某一个资源的坐标,通常我们可以去 Maven 的仓库进行查询:https://mvnrepository.com/ ,在该网站中可直接搜索想要的资源,然后就能得到该资源的坐标。

示例:

  1. 输入资源名称进行检索:

image

  1. 点击你想要的资源进行查看:

image

  1. 选择版本查看坐标:

image

仓库配置

开发者要在自己电脑上做开发,首先要做的就是配置本地仓库。

默认情况下 Maven 本地仓库的位置在哪儿呢?

全局配置:

我们可以选择在全局进行配置,在 Maven 的配置文件conf/settings.xml中可以找到它的说明:

  <!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ${user.home}/.m2/repository
  <localRepository>/path/to/local/repo</localRepository>
  -->

也就是在系统盘当前用户目录下的.m2/repository,比如我当前的系统用户是zs,则默认的本地仓库仓库位置在C:\Users\zs\.m2\repository

因为我们平时开发项目所有的资源会比较多,而且各种资源还有好多的版本,资源与资源之间还有相互依赖的这种情况,因此本地仓库保存的内容会非常的多,它的体积会很大,如果放在 C 盘下不太合适。

因此我们可以自己来指定一个位置作为本地仓库的位置,这个指定同样是需要来修改 Maven 的配置文件conf/settings.xml<localRepository>/path/to/local/repo</localRepository>标签。

这个标签中配置的值就是我们本地仓库的位置,例如:

  <!-- 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:\maven-repository</localRepository>

用户配置:

如果是局部用户配置,那么在仓库的同级目录也可以包含一个settings.xml配置文件,在里面也可以进行指定。

注意:局部用户配置优先于全局配置(遇见相同配置项的时候)

image

注意:Maven 默认连接的远程仓库位置是中央仓库

image

此站点并不在国内,因此有时候下载速度非常慢,因此我们可以配置一个国内站点镜像,可用于加速下载资源。

我们在conf/settings.xml配置文件中找到<mirrors>标签,在这组标签下添加镜像的配置,如下:

<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>central</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

Maven 构建命令

Maven 的构建命令以mvn开头,后面添加功能参数,可以一次性执行多个命令,用空格分离:

  • mvn compile:编译
  • mvn clean:清理
  • mvn test:测试
    • -DskipTests,不执行测试用例,但编译测试用例类生成相应的 class 文件至 target/test-classes 下
    • -Dmaven.test.skip=true,不执行测试用例,也不编译测试用例类
  • mvn package:打包
  • mvn install:安装到本地仓库

IDEA 配置 Maven

不使用原型创建项目

1)在 IDEA 中配置 Maven

image

2)创建 Maven 工程

image

3)填写本项目的坐标

image

4)检查各目录颜色标记是否正确

image

5)IDEA 右侧有一个 Maven 管理界面,可点开查看:

image


使用原型创建项目

普通 Java 工程

1)创建 Maven 项目的时候选择使用原型骨架

image

2)创建完成后发现通过这种方式缺少一些目录,如下图

image

为此,我们需要手动去补全目录,并且要对补全的目录进行标记(切记)

image


Web 工程

1)选择 Web 对应的原型骨架

有很多的 webapp 原型骨架,选择哪个基本都差不多,包括前面创建普通项目也是一样, quickstart 原型也有很多。

image

2)和前面创建普通项目一样,通过原型创建 Web 项目得到的目录结构是不全的,因此需要我们自行补全,同时要标记正确。最终需要得到如下结构:

image

3)Web 工程创建好之后需要启动运行,那么需要使用一个 tomcat 插件来运行我们的项目,在pom.xml中添加插件的坐标即可。最终改好的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.itheima</groupId>
  <artifactId>web01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>web01 Maven Webapp</name>
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

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

  <build>
    <finalName>web01</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.1</version>
      </plugin>
    </plugins>
  </build>
</project>

4)插件配置好后,在 IDEA 右侧maven-project操作面板上可以看到该插件,并且可以利用该插件启动项目

image

运行后该插件会给我们一个可运行地址:

image

如果我们想更换端口,只需要在pom.xml中配置该插件即可:

<plugins>
    <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.1</version>
        <configuration>
            <port>80</port>
        </configuration>
    </plugin>
<plugins>

5)同时为了运行方便,我们也可以创建运行模板

image


Maven 依赖管理

依赖配置与依赖传递

依赖是指在当前项目中运行所需的 jar,依赖配置的格式如下图:

image

依赖传递

依赖具有传递性,分两种:

  1. 直接依赖:在当前项目中通过依赖配置建立的依赖关系。

  2. 间接依赖:被依赖的资源如果依赖其他资源,则表明当前项目间接依赖其他资源。

注意:直接依赖和间接依赖其实也是一个相对关系。

image

依赖传递的冲突问题:

在依赖传递过程中产生了冲突时,有三种优先法则:

  1. 路径优先:当依赖中出现相同资源时,层级越深,优先级越低,反之则越高。

  2. 声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖靠后的。

  3. 特殊优先:当同级配置了相同资源的不同版本时,后配置的覆盖先配置的。

image

可选依赖

image

排除依赖

image


依赖范围

依赖的 jar 默认情况可以在任何地方可用,可以通过scope标签设定其作用范围。

这里的范围主要是指以下三种范围:

  1. 主代码范围内有效(src/main 目录范围内)

  2. 测试代码范围内有效(src/test 目录范围内)

  3. 打包(package 指令范围内)

此外,scope标签的取值有四种:compile、test、provided、runtime

这四种取值与范围的对应情况如下:

image

依赖范围的传递性

image


Maven 生命周期

Maven 构建的生命周期,描述的是一次构建过程中经历了多少个事件。

比如我们项目最常用的一套流程如下:

image

当然 Maven 的生命周期不止这一套,总共分为 3 套,每套里面包含的事件如下:

1)clean:清理工作

  1. pre-clean:执行一些在 clean 之前的工作。
  2. clean:移除上一次构建产生的所有文件。
  3. post-clean:执行一些在 clean 之后立刻完成的工作。

2)default:核心工作。例如编译、测试、打包、部署等

这里面的事件非常得多,如下图:

image

对于 default 生命周期,每个事件在执行之前都会将之前的所有事件依次执行一遍。

3)site:产生报告,发布站点等

  • pre-site:执行一些在生成站点文档之前的工作。
  • site:生成项目的站点文档。
  • post-site:执行一些在生成站点文档之后完成的工作,为部署做准备。
  • site-deploy:将生成的站点文档部署到特定的服务器上。

Maven 插件

前面我们讲了 Maven 生命周期中的相关事件,那这些事件是谁来执行的呢?答案是 Maven 的插件。

插件:

  • 插件与生命周期内的阶段绑定,在执行到对应生命周期时执行对应的插件。
  • Maven 默认在各个生命周期上都绑定了预先设定的插件来完成相应功能。
  • 插件还可以完成一些自定义功能。

插件的配置方式如下:

image

在 Maven 官网中有对插件的介绍:http://maven.apache.org/plugins/index.html


maven-surefire-plugin

什么是 maven-surefire-plugin ?

如果你执行过 mvn test 或者执行其他 maven 命令时跑了测试用例,你就已经用过 maven-surefire-plugin 了。maven-surefire-plugin 是 maven 里执行测试用例的插件,不显示配置就会用默认配置。这个插件的 surefire:test 命令会默认绑定 maven 执行的 test 阶段。

maven的生命周期有哪些阶段?

[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, pre-integration-test, integration-test, post-integration-test, verify, install, deploy]

maven-surefire-plugin 的使用

如果说 maven 已经有了 maven-surefire-plugin 的默认配置,我们还有必要了解 maven-surefire-plugin 的配置么?答案是肯定的。虽说 maven-surefire-plugin 有默认配置,但是当需要修改一些测试执行的策略时,就有必要我们去重新配置这个插件了。

插件自动匹配

最简单的配置方式就不配置或者是只声明插件。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.19</version>
</plugin>

这个时候 maven-surefire-plugin 会按照如下逻辑去寻找 JUnit 的版本并执行测试用例。

if the JUnit version in the project >= 4.7 and the parallel attribute has ANY value
    use junit47 provider
if JUnit >= 4.0 is present
    use junit4 provider
else
    use junit3.8.1

插件手动匹配

示例:

    <build>
        <plugins>
            <!-- 该插件能够在运行后自动在target目录生成allure测试结果目录 -->
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
                <configuration>
                    <includes>
                        <!-- 默认测试文件的命名规则:
                            "**/Test*.java"
                            "**/*Test.java"
                            "**/*Tests.java"
                            "**/*TestCase.java"
                            如果现有测试文件不符合以上命名,可以在 pom.xml 添加自定义规则
                        -->
                        <include>**/**.java</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

更多用法

maven-surefire-plugin 更多用法


资源编译导出问题

当需要编译导出 src/main/java 下的配置文件时,需要加以下配置,否则默认不导出。

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

posted @ 2021-10-19 00:25  Juno3550  阅读(71)  评论(0编辑  收藏  举报