Maven基础概念与使用(一)

前言

为什么需要自动化构建工具

java应用编译构建工程

一个Java项目要部署上线,最简单的构建过程都要经过编译、打包,项目比较少并且互相没有依赖时,我们可以手动调用javac命令编译java源文件,调用jar命令将class文件打包,当工程项目非常多, 并且很多有依赖关系,甚至一些复杂的项目还有很多流程需要处理,如Junit测试、资源过滤处理等等,每次手动机械式操作效率非常低。

本地开发时,本地的IDE工具如Idea、Eclipse、Vscode等代替我们做了大量的工作,当项目上线时, 线上服务器并没有IDE工具,因此需要自动化构建工具来完成项目的构建打包。

Java自动化构建工具的三驾马车

java自动化构建工具

  • Ant
    完全开放的程序式构建工具,没有任何约定,所有的构建过程需要开发人员全部编写在 build.xml配置文件中,过程非常繁琐,并且不支持依赖统一管理。
    build.xml示例

        <?xml version="1.0" encoding="UTF-8"?>
        <!-- name是当前工程的名称,default是默认执行的任务,basedir是工作目录 -->
        <project name="ExampleApp" default="run" basedir=".">
          <!-- 源代码目录 -->
          <property name="src" value="src"/>
          <!-- 编译后的字节码文件目录-->
          <property name="dest" value="classes"/>
          <!-- 最终生成的jar包 -->
          <property name="finalJarName" value="example-app-1.0.jar"/>
          <!-- 定义了一个init任务 -->
          <target name="init">
          <!-- 建立classes目录 -->
            <mkdir dir="${dest}"/>
          </target>
          <!-- 定义编译任务,依赖了init任务 -->
          <target name="compile" depends="init">
            <javac srcdir="${src}" destdir="${dest}" classpath="..."/>
          </target>
          <!-- 打jar包 -->
          <target name="build" depends="compile">
            <jar destfile="${finalJarName}" basedir="classes">
              <!-- 添加MANIFEST -->
              <manifest>
                <attribute name="Created-By" value="example"/>
                <attribute name="Main-Class" value="com.example.train.App"/>
                <attribute name="Class-Path" value="lib/guava-18.0.jar;xxx.jar"/>
              </manifest>
            </jar>
          </target>
          <!-- 运行 -->
          <target name="run" depends="build">
            <java classname="com.example.train.App" classpath="${finalJarName}" />
          </target>
        </project>
    
    • Maven
      XML声明式构建工具,引入了生命周期概念,支持依赖管理,大部分的构建工作(如编译、打包)都可通过生命周期阶段绑定的默认插件来完成,不允许在XML中编写任何自定义任务,必须通过插件来实现。
      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/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.example.train</groupId>
      <artifactId>boot-jar-test-demo</artifactId>
      <packaging>jar</packaging>
      <version>1.0</version>
      <dependencies>
        <dependency>
          <groupId>com.google.guava</groupId>
          <artifactId>guava</artifactId>
          <version>18.0</version>
        </dependency>
      </dependencies>
      <!-- 自定义构建过程 -->
      <build>
        <plugins>
          <plugin>
            ...
          </plugin>
        </plugins>
      </build>
    </project>
    
  • Gradle
    综合了Ant与Maven两者的特点,配置文件语法简洁,可以自由定义task,同时又支持插件来完成大部分构建工作,相比Maven,它还支持Groovy、Kotlin、Scala、C++、Android等项目的构建,它在性能、用户体验上更好,但是它很多优秀理念与约定都来自于Maven。
    build.gradle示例

    buildscript {
        ext {
            springBootVersion = '2.0.3.RELEASE'
            }
        dependencies {
            classpath("org.springframework.boot:spring-boot-gradleplugin:${springBootVersion}")
        }
    }
    // 定义变量
    def env = System.getProperty("profile") ?: "local"
    dependencies {
        compile project(":container-api")
        compile (group: 'org.apache.httpcomponents', name: 'httpclient',version: '4.5.10') {
            force = true
        }
    }
    tasks.withType(JavaCompile) {
        options.encoding = "UTF-8"
    }
    bootJar{
        baseName = 'example-app'
    }
    // 注册一个task
    tasks.register('removeInput', Delete) {
        delete 'inputs/3.txt'
    }
    

    Maven到底是什么

    Maven是目前最流行的Java项目自动化构建工具,它通过定义POM(Project Object Model)和一系列插件来标准化整个项目的构建,同时它又是一个项目管理工具,通过Maven可以整合与拆分项目模块(Module),解决项目之间的依赖关系。

    安装

    访问Maven官方网站 https://maven.apache.org/ , 进入 Download 页面, 选择maven3的稳定版本进行下载,当前项目中使用比较多的应该是3.5.0版本,可进入 https://archive.apache.org/dist/maven/maven-3/3.5.0/binaries/ 选择对应的系统版本进行下载。安装Maven之前,请先安装JDK,同时配置JAVA_HOME环境变量。

    Linux

    首先通过命令工具 wget下载 apache-maven-3.5.0-bin.tar.gz,或者本地下载后直接上传到服务器。

    • 解压
    tar -zxvf apache-maven-3.5.0-bin.tar.1 gz -C /usr/local
    
  • 配置环境变量,通过命令 vim /etc/profile 打开文件,在末尾增加如下配置

    MAVEN_HOME=/usr/local/apache-maven-3.5.0
    PATH=$PATH:$MAVEN_HOME/bin
    MAVEN_OPTS="-Xms256m -Xmx512m"
    export MAVEN_HOME
    export PATH
    export MAVEN_OPTS
    
    • 刷新 /etc/profile
    source /etc/profile
    
  • 验证安装

    mvn -v
    

    Windows

    下载 apache-maven-3.5.0-bin.zip ,将解压后的内容提取到指定的目录

    windows目录

    • 配置环境变量
      进入 桌面->电脑->右键属性->高级系统设置->高级->环境变量, 在系统环境变量中增加一条 MAVEN_HOME=D:\tools\apache-maven-3.5.0,同时在path环境变量中 新增Maven路径 %MAVEN_HOME%/bin

    • 验证
      打开一个新的CMD终端,输入如下命令验证是否成功安装

    mvn -v
    Apache Maven 3.5.0 (ff8f5e7444045639af65f6095c62210b5713f426; 2017-04-
    04T03:39:06+08:00)
    Maven home: D:\tools\apache-maven-3.5.0\bin\..
    Java version: 1.8.0_191, vendor: Oracle Corporation
    Java home: C:\Program Files\Java\jdk1.8.0_191\jre
    Default locale: zh_CN, platform encoding: GBK
    OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
    

基础配置与概念

POM

Project Object Model(POM),项目对象模型,POM将一个项目抽象为具体的模型,然后Maven通过POM来对项目进行管理。POM的详细配置来主要自于Maven项目根目录下的pom.xml文件,但项目构建时最终生效的POM内容可能来自于三个模块的合成。

pom

对于相同配置项,项目pom.xml的优先级最高,在项目根目录(pom.xml文件所在同级目录)下执行命令即可查看最终生效的POM。

mvn help:effective-pom

每个POM默认都隐式继承了Maven定义的超级POM,可通过如下方式查看超级POM内容

默认约定

Maven项目的基本目录结构

D:\campus\workspace\use-plugin-demo>tree
├─src
│ ├─main
│ │ ├─java
│ │ └─resources
│ └─test
│ ├─java
│ └─resources
└─target
├─classes

Maven的目标是尽可能将项目的构建过程自动化,因此默认约定以下目录结构,默认约定都是在超级POM中定义的。

pom

注意: 不要在项目pom.xml中覆盖超级POM定义的默认目录结构,因为Maven的核心理念是约定大于配置,配置大于编码。

Artifact

artifact在Maven中可以是任何文件,大部分情况下可能是POM文件或者JAR文件,只要通过坐标能够将其定位就可以称为artifact,跟Docker Harbor镜像仓库中的artifact类似。artifact有三个非常重要的坐标,简称GAV, 通过GAV可以在同一个仓库中确定唯一的artifact。

pom

  • groupId 公司或组织域名倒序,如 com.example.train
  • artifactId 具体的项目,如 course-demo
  • version 版本,如 1.0
<dependency>
  <groupId>com.example.train</groupId>
  <artifactId>course-demo</artifactId>
  <version>1.0</version>
</dependency

通过这个GAV坐标定位到的文件就是com/example/train目录下的course-demo-1.0.jar,jar文件所在目录通常还会有一个对应的pom文件,也称为元数据文件。

其它几个可选的坐标属性

  • classifier
<dependency>
  <groupId>com.example.train</groupId>
  <artifactId>course-demo</artifactId>
  <version>1.0</version>
  <classifier>jdk7</classifier>
</dependency>

配置了classifier属性后,定位到的文件是com/example/train/course-demo-1.0-jdk7.jar。

  • extension

在pom.xml中一般通过配置type属性,对应的ArtifactHandler会进行处理,下面是常用的部分type类型。

pom

当在pom.xml中进行如下配置时,也能定位到文件com/example/train/course-demo-1.0-jdk7.jar。

<dependency>
  <groupId>com.example.train</groupId>
  <artifactId>course-demo</artifactId>
  <version>1.0</version>
  <type>test-jar</type>
</dependency>

此时ArtifactHandler是test-jar,classifier是tests,extension是jar,通过type属性可以辅助定位到artifact以及是否加入classpath等,不填写type默认jar。

仓库

Maven使用仓库来存储artifacts(jar),项目中的依赖包都是从仓库中下载,在项目中构建好的artifact也可以安装上传到仓库中。

本地仓库(Local Repository)

  • 位于本机文件系统目录,主要用来缓存从远程仓库下载的依赖包,通过mvn install命令也可将构建好的jar存放于本地仓库。
  • Maven安装后,本地仓库默认位置是${user.home}/.m2/repository,由于用户目录一般位于系统盘,在项目开发过程中,本地仓库缓存的依赖包会非常庞大,强烈建议修改默认位置。

中央仓库(Central Repository)

远程仓库(Remote Repository)

远程仓库是相对于本地仓库而言,需要通过网络访问的仓库都称为远程仓库,中央仓库也是一种远程仓库。

  • 私有仓库(局域网):公司内网搭建的Nexus私服
  • 开放仓库(公网):代理了中央仓库,用户就近访问,提高下载速度,如阿里云仓库、红帽子仓库等。

repo

如图所述,世界各地任何项目构建时,任何依赖都需要请求中央仓库,这会给中央仓库带来巨大的访问压力,因此Maven又推出了Mirror(镜像)配置策略,给某个仓库A配置了Mirror,那Maven构建项目时不会去请求A,而是请求A仓库配置的Mirror仓库。

在国内,中央仓库网络几乎访问不通,因此通常给中央仓库配置一个Mirror,加快访问速度,这个Mirror仓库可以是国内的公网远程仓库,也可以是内网的私服仓库。

<mirrors>
  <mirror>
    <id>innerNexus</id>
    <!-- 给中央仓库配置Mirror, 这个id代表的就是中央仓库,在超级POM中定义的id -->
    <mirrorOf>central</mirrorOf>
    <name>inner nexus server</name>
    <url>http://120.99.18.186:8880/repository/maven-public/</url>
  </mirror>
</mirrors>

通过GAV坐标在同一个仓库可以确定唯一artifact,如果混用多个远程仓库可能jar包不兼容。

Settings

Maven配置settings通常有两种级别

  • 全局settings
    Maven安装时自动生成的,${MAVEN_HOME}/conf/settings.xml。

  • 用户settings
    通常位于${user.home}/.m2/settings.xml

两个settings文件同时存在,它们的内容将进行合并,对于相同配置项用户settings配置优先。

<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 https://maven.apache.org/xsd/settings-1.0.0.xsd">
  <!-- 本地仓库地址 -->
  <localRepository/>
  <!-- 当maven需要输入值的时候, 是否交由用户输入, 默认为true -->
  <interactiveMode>true</interactiveMode>
  <!-- 基于安全性或者网络问题,可设置为离线模式, 默认false -->
  <offline/>
  <!-- 插件的groupId,当maven执行未指定groupId的插件命令时在此列表搜索, mvn test:test-->
  <!-- 不配置pluginGroup,则要指定完整插件坐标执行 mvn com.example:test-mavenplugin:v1.0:test -->
  <pluginGroups/>
  <!-- 远程仓库服务器的访问凭证信息 -->
  <servers/>
  <!-- 镜像加速 -->
  <mirrors/>
  <!-- 网络代理 -->
  <proxies/>
  <!-- Profile属性列表 -->
  <profiles/>
  <!-- 激活的Profile配置 -->
  <activeProfiles/>
</settings>

在settings文件中可以编写插值表达式,表达式内容通常来自于系统属性(System.getProperties)或者环境变量。

<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 https://maven.apache.org/xsd/settings-1.0.0.xsd">
  <localRepository>${user.home}/.m2/repository</localRepository>
  <offline>false</offline>
  ......
</settings>

在开发maven项目过程中,需要在settings.xml中编写的常用配置如下

  • 本地仓库路径
<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
https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <localRepository>E:/m2/repo</localRepository>
  ...
</settings>

不配置,默认 ${user.home}/.m2/repository

  • Servers

远程仓库的依赖下载与上传通常在项目的pom.xml中定义,但是远程库所在的服务器信息,如访问用户名、密码等,往往因为不适合与POM一起发布,所以一般配置在settings文件中。

<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
https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <servers>
    <server>
      <id>example_snapshot</id>
      <username>user</username>
      <password>6dc85a12</password>
    </server>
    <server>
      <id>example</id>
      <username>user</username>
      <password>6dc85a12</password>
    </server>
  </servers>

server中的id自定义不重复就行,该id与pom文件中distributionManagement中repository元素的id相匹配,同时与当前settings中的mirror中的id相匹配,除了username,password属性,还可以定义文件目录权限、密钥key路径。

  • Mirrors
<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
https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <mirrors>
    <mirror>
      <id>nexus-aliyun</id>
      <name>Nexus aliyun</name>
      <url>https://maven.aliyun.com/repository/central</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
  ...
</settings>

mirror相当于一个拦截器,它会拦截maven对remote repository的相关请求,把请求里的remote repository地址,重定向到mirror里配置的地址。id用于区分mirror,这个id与Servers中的id相匹配,当需要使用相关凭据连接远程库时,根据id在servers配置中查找。

Mirror配置规则

  • 匹配所有仓库请求,即将所有的仓库请求都转到该镜像上
<mirrorOf>*</mirrorOf>
  • 仓库repo1和repo2的请求转到该镜像上,使用逗号分隔多个远程仓库
<mirrorOf>repo1,repo2</mirrorOf>
  • 匹配所有仓库请求,repo1除外,使用感叹号将仓库从匹配中排除
<mirrorOf>*,!repo1</miiroOf>

通常配置central镜像即可,我们也可以配置一个所有仓库的镜像,以保证该镜像是Maven唯一使用的仓库。

pom与settings中定义的仓库与镜像优先级关系

mirror(settings.xml)> repository(pom.xml)> repository(settings.xml)
  • Profiles

Profile可以定义一系列的配置信息,然后指定其激活条件。通过定义多个profile,根据不同的激活条件从而达到不同环境使用不同的构建配置信息。

  • settings.xml 中的 profile 元素是 pom.xml 中 profile 元素的裁剪版本。
  • settings.xml 负责的是整体的构建过程, pom.xml 负责单独的项目对象构建过程。
  • settings.xml 只包含了id, activation, repositories, pluginRepositories 和 properties 元素,不可自定义标签元素。
  • settings.xml与pom.xml定义了相同id的profile,内容进行合并叠加,settings中配置优先级更高。

查看当前激活的profile

mvn help:active-profiles

命令行结构

Maven的核心是POM,除了少数几个帮助性命令(help, version)可以在任何地方执行,其它的命令都必须在项目根目录下执行,即编写了pom.xml的目录。

mvn [options] [<goal(s)>] [<phase(s)>]

pom