【Maven】自动化构建工具:Maven

403 阅读6分钟

1. Maven 基础

1.1 Maven 安装

Maven 的下载和安装过程这里就不在累述,下面是 Maven 安装完毕后需要配置的环境变量

配置环境变量

(1) 添加变量M2_HOME,其值为 Maven的安装目录,例如:D:\Program Files (x86)\Maven\maven-3.6.1
(2) 将M2_HOME添加到系统变量PATH,即在PATH中追加 %M2_HOME%/bin;

1.2 Maven 配置

(1) 配置本地仓库

修改 settings.xml 文件中的 localRepository 标签,用来指定本地仓库,如下所示:

<localRepository>D:\Program Files (x86)\Maven\maven-3.6.1\repository</localRepository>

(2) 配置镜像

有的时候 Maven 的中央仓库下载文件会很慢,我们可以配置国内的 Maven 仓库镜像,这样下载文件就非常迅速。下面是配置阿里云的镜像

<mirrors>
    <mirror>
        <id>aliyunmvn</id>
        <name>aliyunmvn</name>
        <indexOf>public</indexOf>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
</mirrors>

注意: 有的同学在配置阿里云的镜像时,将indexOf标签的值设置为**表示匹配任意的远程仓库,这可能会导致用户在 pom.xml 文件中指定的远程仓库无效。为了避免这种情况,可以将indexOf设置为具体的仓库名称。

阿里云的Maven仓库:

1.3 Maven 工程目录结构

使用 Maven 创建工程的时,默认的工程目录结构如下所示:

|---src
|---|---main
|---|---|---java
|---|---|---resources
|---|---test
|---|---|---java
|---|---|---resources
|---pom.xml
  • src/main/java:存放主程序的类文件
  • src/main/resources:存放主程序的资源文件,例如:配置文件、静态资源文件等
  • src/test/java:存放测试程序的类文件
  • src/test/resources:存放测试程序的资源文件,例如:配置文件,静态资源文件等
  • pom.xml:pom文件

1.4 资源检索

当你在引入依赖但却不知道依赖的坐标时,你可以访问https://mvnrepository.com,然后通过关键字可以查询所需要资源信息。

2. Maven的生命周期、命令、插件

2.1 生命周期

Maven 的生命周期本质上是对项目构建过程的抽象,Maven 定义了 3 套生命周期:cleandefaultsite。每个生命周期又包含很多阶段,即phase,例如:清理、编译、测试、打包、部署等等...,这些phase都可以通过 Maven 相关的命令来完成,但是,实际上都是通过对应的 Maven 插件来实现的。

Tips:在项目开发过程中,我们最常用的生命周期就是default,这也是Maven最核心的生命周期。

2.2 Maven 基础命令

mvn clean:清理原先编译生成的目录,即 target目录,但是已经 install 到本地仓库的依赖包不会被删除

mvn compile:编译主程序,会生成一个target目录,编译后的字节码文件存放在 target /classes目录

mvn test-compile:编译测试程序,会生成一个 target目录,编译后的字节码文件存放在test-classes目录

mvn test:进行测试,测试结果会放在target/surefire-reports目录

mvn package:打包主程序,执行该命令会先编译再打包,打包的结果默认存放在target目录

mvn install:安装主程序,执行该命令会先编译打包再 install,install 的目的是将打包的结果存放在本地仓库,可供其它项目使用

2.3 Maven 常用插件

Maven 是一个插件框架,Maven 生命周期的每一个阶段都是由不同的插件来完成的。例如,在编译阶段,需要maven-compiler-plugin插件来指定编译时采用的 JDK 版本;在打包阶段,需要maven-jar-plugin插件或者maven-war-plugin插件来完成项目的打包工作,等等....

正因如此,我们需要对一些常用的 Maven 插件进行学习和总结,以便在今后的工作中能正确地使用 Maven,从而提开发效率。

(1) maven-compiler-plugin 插件

maven-complier-plugin 插件用来指定编译阶段采用的 JDK 版本以及编码格式。如果不显示地指定,则在编译阶段会使用maven-complier-plugin插件默认的 JDK 进行编译,这可能会导致项目编译失败。例如,代码中使用了 Java 8 的新特性,但是在编译的时候使用 JDK 6,因此导致编译失败。

示例:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.2</version>
            <configuration>
                <!--设置源码JDK版本-->
                <source>1.8</source>
                <!--设置编译采用的JDK版本-->
                <target>1.8</target>
                <!--设置字符编码-->
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

(2) maven-jar-plugin 插件

maven-jar-plugin 是 Maven 默认的打包插件,这个插件会将可执行的 jar 包与依赖包分开。将打包后的 jar 包存放在target目录中,将依赖包存放在target/lib 目录中,并且可执行的 jar 包与 lib 目录在同一级。

使用maven-jar-plugin插件打包后,在得到的 jar 包内的MATE-INF目录下会生成一个MANIFEST.MF文件,这个文件主要是记录了当前 jar 包在执行时读取的 classpath,以及依赖包的位置等信息。

示例:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <!--归档-->
                <archive>
                    <!--配置清单-->
                    <manifest>
                        <!--指定入口程序-->
                        <mainClass>com.example.Application</mainClass>
                        <!--是否将依赖包的classpath添加到当前jar的classpath中-->
                        <addClasspath>true</addClasspath>
                        <!--依赖包的存放目录-->
                        <classpathPrefix>lib/</classpathPrefix>
                    </manifest>
                    <!--追加当前jar可读取的classpath-->
                    <manifestEntries>./</manifestEntries>
                </archive>
                <!--排除不需要一起打包的文件-->
                <excludes>
                    <exclude>省略</exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

(3) maven-resource-plugin 插件

maven-resource-plugin插件在默认情况下只会读取src/main/resources目录下的所有文件,并且在编译的时候会将 src/main/resources目录下的所有文件拷贝到 target/classes目录下。但是,如果你的资源文件在其它目录,那么就需要配置maven-resource-plugin插件,让其读取指定的资源文件。

示例:

<build>
    <plugins>
    	<plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.6</version>
        </plugin>
    </plugins>
    
    <!-- 资源配置 -->
    <resources>
        <!-- 读取src/main/resources目录下所有以.properties、.xml、.yml、.yaml结尾的文件 -->
        <resource>
            <directory>src/main/resources/</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
                <include>**/*.yml</include>
                <include>**/*.yaml</include>
            </includes>
        </resource>
        <!-- 读取src/main/resources目录以.xml结尾的文件 -->
        <resource>
        	<directory>src/main/java/</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

更多插件的学习可以参考官网 Maven Plugins 部分

3. 依赖管理

3.1 依赖的范围

在 pom.xml 文件中引入依赖的时候,通过<scope>标签来指定依赖的作用范围,依赖的作用范围用来控制依赖包在哪些phase生效,<scope>标签的可选值如下所示:

  • compile:默认的依赖范围,如果使用 compile,依赖包在主程序和测试程序的编译与运行阶段都有效
  • test:测试依赖范围,如果使用 test,依赖包在主程序的编译、运行阶段不会生效,而在测试程序的编译、运行阶段有效;例如:在引入 junit 依赖时,其依赖范围就是 test
  • provided:provided依赖范围,如果使用 provided,依赖包在主程序和测试程序的编译阶段生效,而在运行阶段不会生效;例如:引入 javax.servlet-api 依赖时,其依赖范围就是 provided,因为在运行时 Tomcat 容器会提供 javax.servlet-api 依赖
  • runtime:运行时依赖范围,如果使用runtime,依赖包在主程序和测试程序的编译阶段不会生效,而在运行阶段才会生效
  • import:导入依赖范围,这个依赖范围只能在<dependencyManagement>标签下使用才能生效
  • system:跟 provided 基本一致,不常用,可忽略

3.2 依赖的传递性

假设有 A、B、C 三个项目,A 依赖 B,B 依赖 C,则 A 依赖 C;Maven 依赖传递性描述如下:

A->B; B->C => A->C

依赖的传递性对依赖作用范围的影响:

compiletestprovidedruntime
compilecompile--runtime
testtest--test
providedprovided-providedprovided
runtimeruntime--runtime

上述表格中,最左边一列的依赖范围为第一直接依赖的依赖范围,最上面一行的依赖范围为第二直接依赖的依赖范围,现总结规律如下:

  • 如果第二直接依赖的依赖范围为compile,则传递的依赖的作用范围与第一直接依赖一致
  • 如果第二直接依赖的依赖范围为test,则依赖不可传递
  • 如果第二直接依赖的依赖范围为provided,则只有第一直接依赖的依赖范围也为provided时,依赖才会传递
  • 如果第二直接依赖的依赖范围为runtime,则传递的依赖的作用范围与第一直接依赖一致,除第一直接依赖的依赖范围为compile

参考文档