Maven

一 介绍

构建一个项目通常由多个任务组成, 如下载依赖,放入classpath下,编译源码,运行测试,打包,部署等. 而maven则是一个自动化这些任务的工具.

Maven核心上是一个执行插件的框架,所有的工作都由插件完成。插件提供了很多goal,goal可以挂在在构建的不同生命周期中运行. 在super pom(见2.2.2小节)中提供了默认插件, 以提供maven基本功能.

生命周期是项目构建的主要方向, Maven有三个内置的生命周期:

  • clean: 用于清理项目
  • default: 编译部署项目
  • site: 生成网页文档

一个生命周期由一系列phase(阶段)组成,当maven执行某个phase时,如mvn package, 会按顺序执行之前的phase和该phase。实际上phase会被映射到底层的goals,也就是说,真正执行的是goals,并且一个phase可以执行多个goals。根据项目的不同打包类型,每个phase使用的goals可能不同。为了执行内置的生命周期,一些插件会默认被maven使用。

具体关于Lifecycle vs. Phase vs. Plugin vs. Goal的区别,请看参考链接

为了方便生成项目, maven提供了一堆模板供使用, 如mvn archetype:generate ...(一堆参数)archetype是插件提供给这个goal的前缀.

maven有两种类型仓库: 依赖仓库和插件仓库. 从来源也可以将仓库分为本地和远程仓库, 当构建项目时, maven会先从本地查找依赖, 无则从远程仓库查找依赖.

maven项目有个固定的标准的目录结构, 也是super pom中设置好的.

maven项目的配置由pom文件提供, pom文件中所有元素清单如下:

<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">
  <!-- pom版本, 必须 -->
  <modelVersion>4.0.0</modelVersion>
 
  <!-- The Basics -->
  <groupId>...</groupId>
  <artifactId>...</artifactId>
  <version>...</version>
  <packaging>...</packaging>
  <dependencies>...</dependencies>
  <parent>...</parent>
  <dependencyManagement>...</dependencyManagement>
  <modules>...</modules>
  <properties>...</properties>
 
  <!-- Build Settings -->
  <build>...</build>
  <reporting>...</reporting>
 
  <!-- More Project Information -->
  <name>...</name>
  <description>...</description>
  <url>...</url>
  <inceptionYear>...</inceptionYear>
  <licenses>...</licenses>
  <organization>...</organization>
  <developers>...</developers>
  <contributors>...</contributors>
 
  <!-- Environment Settings -->
  <issueManagement>...</issueManagement>
  <ciManagement>...</ciManagement>
  <mailingLists>...</mailingLists>
  <scm>...</scm>
  <prerequisites>...</prerequisites>
  <repositories>...</repositories>
  <pluginRepositories>...</pluginRepositories>
  <distributionManagement>...</distributionManagement>
  <profiles>...</profiles>
</project>

二 基础

2.1 项目坐标

用于区分项目

  • groupId:组织或公司唯一标识, 常使用倒置域名, 如com.baidu
  • artifactId:项目名, 即组织或公司开发的项目的名字, 如my-project
  • version:版本号。如2.0,2.0.1,1.3.1,而SNAPSHOT表示正在开发.

这些参数仅用于标识项目, 不会对项目结构造成影响, 如package结构. 但会影响maven仓库中项目的存储结构, 如$M2_REPO/com/baidu/my-project/1.0

packaging

定义项目打包时生成文件的类型, 默认jar, 所有可选值: pom, jar, maven-plugin, ejb, war, ear, rar. packaging的选择会影响整个生命周期中执行的goals

打包时, 默认会将依赖加入目标包中

2.2 POM关系

项目之间有依赖,继承,多模块关系. 如一个项目依赖另一个项目; 子项目继承父项目, 父项目多用于为子项目管理依赖; 多模块项目的顶层项目主要用于将多个模块(项目)分成一组, 只需对顶层项目构建, 所有模块都会被构建. 一般复杂项目中会同时存在这三种关系.

2.2.1 dependencies

一个简单的例子:

<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
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <type>jar</type>
      <scope>test</scope>
      <optional>true</optional>
    </dependency>
    ...
  </dependencies>
  ...
</project>
  • groupId, artifactId, version: 三个坐标确定一个依赖.

  • type: 依赖类型, 默认jar

  • scope: 指定依赖什么时候在classpath中可见, 不同类型的scope还会影响它的传递性.

    scope编译时测试时运行时传递性注意
    compile(default)提供提供提供
    provided提供提供一般JDK或容器已提供
    servlet-api
    runtime提供提供应该有编译时不需要, 用于辅助
    spring-boot-devtools
    test提供用于测试代码
    junit
    system提供提供类似provided, 但Jar在系统
    其他地方提供, 需要配合
    systemPath使用
  • optional: 直接影响依赖的传递性, true时无传递性, 默认false

  • systemPath: 仅当scopesystem时才生效, 指定该依赖的位置.

版本定义规则(了解)
  • 1.0: “Soft” requirement on 1.0 (just a recommendation, if it matches all other ranges for the dependency)
  • [1.0]: “Hard” requirement on 1.0
  • (,1.0]: x <= 1.0
  • [1.2,1.3]: 1.2 <= x <= 1.3
  • [1.0,2.0): 1.0 <= x < 2.0
  • [1.5,): x >= 1.5
  • (,1.0],[1.2,): x <= 1.0 or x >= 1.2; multiple sets are comma-separated
  • (,1.1),(1.1,): this excludes 1.1 (for example if it is known not to work in combination with this library)

还有什么版本顺序, 不知道啥子用, 见Version Order

排除

当传递依赖冲突时, 可以使用exclusion元素, 排除某个依赖的传递依赖

<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
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-embedder</artifactId>
      <version>2.0</version>
      <exclusions>
        <exclusion>
          <groupId>org.apache.maven</groupId>
          <artifactId>maven-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    ...
  </dependencies>
  ...
</project>

或者使用*排除某个依赖的所有依赖

<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
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-embedder</artifactId>
      <version>3.1.0</version>
      <exclusions>
        <exclusion>
          <groupId>*</groupId>
          <artifactId>*</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    ...
  </dependencies>
  ...
</project>

2.2.2 继承

pom之间是可以继承的, 子pom可继承的元素有:

  • groupId
  • version
  • description
  • url
  • inceptionYear
  • organization
  • licenses
  • developers
  • contributors
  • mailingLists
  • scm
  • issueManagement
  • ciManagement
  • properties
  • dependencyManagement
  • dependencies
  • repositories
  • pluginRepositories
  • build
    • plugin executions with matching ids
    • plugin configuration
    • etc.
  • reporting
  • profiles

不能继承的有:

  • artifactId
  • name
  • prerequisites

比如, 子pom可以从父pom中继承依赖(dependencies).

每个pom都有父pom, 没有声明时会隐式继承Super POM, 从中可以看到pom的一些默认设置, 如默认的标准项目结构, 默认运行所必须的插件等.


使用步骤如下:

  1. 父pom声明packaging元素为pom

    <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
                          https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
     
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>my-parent</artifactId>
      <version>2.0</version>
      <packaging>pom</packaging>
    </project>
    
  2. 子pom中声明父pom

    <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
                          https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
     
      <parent>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>my-parent</artifactId>
        <version>2.0</version>
        <relativePath>../my-parent</relativePath>
      </parent>
     
      <artifactId>my-project</artifactId>
    </project>
    

    relativePath指定父pom位置, 默认../pom.xml

    子pom查询父pom的顺序: relativePath–>本地仓库–>远程仓库

dependencyManagement

通过该元素可以管理所有子pom的依赖信息. 它和dependencies不同, 子pom会从父pom中继承dependencies声明的依赖, 而至于dependencyManagement, 则子pom中存在dependencyManagement的依赖时, 会从该元素中获取信息, 如版本号(version)和作用于(scope), 因此子pom中可以省略这些信息. 在spring boot项目中该用法十分常见.

2.2.3 多模块

多模块项目(也叫Aggregation项目), 由多个模块组成, 每个模块都是一个完整的项目. 这样的好处是将多个项目当作一个组, 执行maven生命周期时, 如打包, 所有的项目都会被打包 , 并且maven会处理好不同项目之间的依赖关系而选择正确的打包顺序, 如先打包dao项目, 后service项目等等.

使用: 首先多模块项目的packaging声明为pom, 然后添加modules元素, 如下所示:

<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
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>my-parent</artifactId>
  <version>2.0</version>
  <packaging>pom</packaging>
 
  <modules>
    <module>my-project</module>
    <module>another-project</module>
    <module>third-project/pom-example.xml</module>
  </modules>
</project>

module元素中填项目的相对路径或这些项目的pom文件地址


关于继承和多模块

通常继承和多模块会同时存在于一个顶层项目中, 但是他们没有必然关系, 即被继承的父pom项目可以不存在多模块, 多模块项目可以不被子模块继承.

2.3 属性(Porperties)

属性是一个占位符, 可以在pom中其他地方使用, 如:

<project>
  ...
  <properties>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  </properties>
  ...
</project>

然后可以通过${x}来使用它, 如${maven.compiler.source}

三 生命周期

三个生命周期所有的phase如下所示:

  • clean:pre-clean, 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,
    pre-integration-test, integration-test, post-integration-test, verify, install, deploy
  • site:pre-site, site, post-site, site-deploy

default lifecycle常用的phases:

在这里插入图片描述

所有常用的phases如下(来自idea截图):

在这里插入图片描述

四 其他

4.1 父、子POM(废弃)

该小节冗余, 将废弃, 但是不忍心删除这些内容

一个项目可以由多个相对独立的模块(小项目)组成,每个模块可以存在一个pom文件。为了防止模块之间冗余,父pom抽离子pom间的公共部分,由子pom继承父pom配置,使项目更容易维护。

当父、子pom中属性或依赖冲突时,子pom优先级高。

4.1.1 定义

  • 父pom:通过在pom中定义<packaging>pom</packaging>来声明;

  • 子pom:pom中添加parent元素,如:

        <parent>
            <groupId>top.sidian123.demo</groupId>
            <artifactId>MavenExamples</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
    

    parent元素中可定义relativePath元素,表明父pom相对于当前子pom的位置。默认../pom.xml,如果不填,则从仓库中查找。

4.1.2 依赖查找

  1. 先在父pom中查找依赖
  2. 在本地仓库中查找
  3. 最后查找远程仓库

参考:Maven – Parent and Child POM Example

4.1.3 dependencyManagement

dependencyManagement用在父pom文件中,只有子pom中存在dependencyManagement中的依赖,才会从该依赖中继承未指定的配置,如spring boot中的版本号。

4.2 资源插件

maven默认资源插件负责将项目中资源拷贝到输出目录中。默认资源放在src/main/resources中。

  • 更改默认资源文件夹

    <build>
       ...
       <resources>
         <resource>
           <directory>src/my-resources</directory>
         </resource>
       </resources>
       ...
    </build>
    
  • 多个资源目录

    <build>
    	...
       <resources>
         <resource>
           <directory>resource1</directory>
         </resource>
         <resource>
           <directory>resource2</directory>
         </resource>
         <resource>
           <directory>resource3</directory>
         </resource>
       </resources>
       ...
    </build>
    
  • 仅引入目录中匹配成功的资源

    <build>
        ...
        <resources>
            <resource>
                <directory>[your directory]</directory>
                <includes>
                  <include>**/*.txt</include>
                  <include>**/*.rtf</include>
                </includes>
            </resource>
            ...
        </resources>
        ...
    </build>
    
  • 引入目录中所有资源,除了匹配成功的

    <build>
        ...
        <resources>
            <resource>
                <directory>src/my-resources</directory>
                <excludes>
                    <exclude>**/*.bmp</exclude>
                    <exclude>**/*.jpg</exclude>
                    <exclude>**/*.jpeg</exclude>
                    <exclude>**/*.gif</exclude>
                </excludes>
            </resource>
            ...
        </resources>
        ...
    </build>
    
  • <includes>引入的资源用<excludes>筛选:

    <build>
        ...
        <resources>
            <resource>
                <directory>src/my-resources</directory>
                <includes>
                    <include>**/*.txt</include>
                </includes>
                <excludes>
                    <exclude>**/*test*.*</exclude>
                </excludes>
            </resource>
            ...
        </resources>
        ...
    </build>
    

参考:Apache Maven Resources Plugin

4.3 插件

总的,插件可以被归为两类:

  • Build plugins:在项目构建时执行;在pom的<build/>元素中配置
  • Reporting plugins:在生成文档时被执行;在pomr的<reporting/>元素中配置

4.4 安装

  1. 首先安装了JDK, 并且设置环境变量JAVA_HOME

    export JAVA_HOME=/home/sidian/Software/jdk-11.0.3
    
  2. 官网上下载最新版Maven并解压

  3. 将Maven的bin目录添加到环境变量中.

  4. 运行mvn --version进行测试

五 依赖进阶

  • 项目依赖除了来源于pom文件中声明的依赖外, 还有从父依赖继承的依赖, 依赖的传递依赖. 因此这里将依赖区分为: 直接依赖, 继承依赖, 传递依赖
  • 依赖间可能会出现依赖版本冲突的问题.

5.1 冲突解决

  1. 最近选择: 当传递依赖冲突时, 依赖树中离项目最近的依赖的版本被选择

    在这里插入图片描述

    最终会使用1.0版本的D依赖

    我们也可以声明直接依赖来解决冲突问题.

  2. 依赖管理(见2.2.2小节): 依赖管理除了可以指定未声明版本的直接依赖的版本外, 传递依赖的版本可以通过dependencyManagement强制确定.

    注意, 该项目的直接依赖继承依赖不能被依赖管理影响.

  3. 排除依赖(见2.2.1小节): 通过exclusion元素可以排除依赖, 如A->B->D, 可以在A中排除D依赖.

  4. 依赖作用域(scope): 声明直接依赖时可以指定它的作用域, scope不仅影响该依赖, 还影响该依赖的传递依赖的有效作用域. 具体规则如下所示:

    compileprovidedruntimetest
    compilecompile(*)-runtime-
    providedprovided-provided-
    runtimeruntime-runtime-
    testtest-test-

    行表示依赖的scope, 列表示传递依赖的scope, 值为传递依赖的有效作用域

    设置直接依赖的不同scope, 来影响传递依赖的有效scope来达到解决冲突的目录, 但是因为不太直观, 因此不建议使用该方法.

5.2 引入版本管理

dependencyManagement元素主要用于管理依赖的版本, 以至于声明依赖时不用写版本号. 通过我们会通过父pom来集中管理依赖, 然后子pom通过继承来获取该元素.

当有多个版本管理pom文件需要引入时会出现问题, 因为父pom只能存在一个. 可以通过scope元素的import值引入, 如下所示:

project ...>
    <modelVersion>4.0.0</modelVersion>
    <groupId>baeldung</groupId>
    <artifactId>Test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Test</name>
     
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>baeldung</groupId>
                <artifactId>Baeldung-BOM</artifactId>
                <version>0.0.1-SNAPSHOT</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

import只能在dependencyManagement元素使用, 并且引入的依赖为pom类型. 上面的例子中引入了Baeldung-BOMdependencyManagement元素来进行依赖管理.

这种方法还是和继承有区别的, import方法仅"继承"dependencyManagement元素.

5.3 有效依赖版本

当依赖在多个地方声明版本时, 到低使用哪个版本呢? 下面给出答案, 优先级由高到低:

  1. 声明直接依赖时指定的版本号
  2. 继承依赖的版本号
  3. 引入(import)pom的版本号, 也考虑引入pom间的顺序
  4. 传递依赖的最近选择算法确定(见5.1)

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值