Maven的使用

本文最后更新于:2020年12月24日 下午

整理自唐浩荣,感谢分享:)

1.概述

Maven的英文翻译为“专家”,“内行”。Maven是Apache下一个纯Java开发的开源项目,它是一个跨平台的项目管理工具,即针对Java平台的项目构建和依赖的管理。Maven提供了开发人员构建一个完整的生命周期框架,从清理、编译、测试、报告、打包、再到最后的部署。简单来说Maven就方便Java项目的构建,同时也方便开发者对项目的管理。

​ Maven在企业中使用非常的广泛,虽然用的很多,但是有些人对于Maven的发音任然不标准。Maven 读音是 [ˈmeɪvn]。可以读作“霉文”或者”麦文”,但是不能读成”妈文”、”某文”等。

(1)项目构建

​ 我们首先要区别的是,项目构建并不是项目创建的意思,项目创建只是一个工程的创建过程,而项目构建过程包括:【清理→编译→测试→报告→打包→部署】这几个步骤,这六个步骤就是一个项目的完整构建过程。

(2)依赖管理

​ 依赖指的是项目与jar包之间的相互依赖,比如我们在使用Spring框架的时候,需要用到Spring依赖的jar包。而依赖管理指的就是使用Maven来管理项目中使用到的jar包,Maven管理的方式就是“自动下载项目所需要的jar包,统一管理jar包之间的依赖关系”。比如在文件上传的时候,光光有common-fileupload这个jar包是不行的,common-fileupload还依赖于common-io这个包jar包,此时由于Maven的依赖管理机制,都会帮我们自动下载这些包,我们并不需要管。

2.下载

下载地址:https://maven.apache.org/download.cgi

下载的zip文件夹目录分别为:

  • bin目录:包含 maven运行时的脚本,这些脚本用来配置Java命令,准备好classpath和相关的java系统属性。其中:mvn是UNIX平台的shell脚本, mvn.cmd 是Windows平台的 bat脚本, mvcDebug 是UNIX的调试脚本, mvcDebug.cmd 是Windows平台的 调试脚本。
  • boot目录:包含类加载器框架plexus-classworlds-2.6.0.jar及其软件许可证。Maven可以使用它,来加载自己的类库。
  • conf目录:是配置文件目录,其中有一个settings.xml 文件,用来配置maven。还有一个 logging文件,日志记录,来配置日志的相关信息。
  • lib目录:maven运行时,所需要的类库。里面放置了大量的 .jar 类库及对应的软件许可证 .license.
  • LICENSE:Maven使用的软件许可证。
  • NOTICE:Maven包含的第三方软件
  • README.txt:Maven的简单介绍,包括安装需求和安装的简要指令。

3.配置

需先按照好对应版本的JDK,可采用java —version查看自己的jdk版本,也可通过IDEA查看。

(1)maven环境变量

首先右击此电脑—>属性—>高级系统设置—>高级—>环境变量。

①、新建系统变量:MAVEN_HOME。变量值:D:\Maven\apache-maven-3.6.3(这是你Maven的存储路径)

②、编辑系统变量:Path。添加变量值:%MAVEN_HOME%\bin。其中“%MAVEN_HOME%\bin”代表的就是”D:\Maven\apache-maven-3.6.3\bin“目录。

③、检验配置是否成功:用win键+R,输入cmd进入dos命令,输入mvn -v,出现如下信息则表明成功!

④、配置运行参数环境变量(可选配置):MAVEN_OPTS。变量值:-Xms128m -Xmx512m。

因为Maven使用来Java来启动的,所以依赖于JVM。所以可以通过JAVA命令参数的方式来设置MAVEN运行参数。

(2)自定义

🔎 修改本地仓库位置

Maven管理的jar包默认是存储在C盘用户目录下的 .m2 下的repository中。

如果你和我一样有强迫症的话(不喜欢把无关系统的东西放在C盘),可以更改Maven的存储位置。

首先我们找到解压的Maven文件,然后打开conf目录下的setting.xml。

大概在55行左右我们加入如下配置文件:

<localRepository>D:/Maven/maven-repository</localRepository>

我把它存放在D盘下。

🔎 修改镜像源

Maven默认下载jar包是从中央仓库下载的,所以我们在国内下载jar包是非常非常的慢,所以我们需要改一下,这里推荐阿里云的仓库。

大概在setting.xml中的第159行左右我们加入如下配置文件:

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

🔎 修改Maven默认的JDK版本

我们在使用编辑软件开发时,编辑软件总是默认给我们设置奇奇怪怪的JDK版本,导致程序总是莫名的报错,这一点我是采过坑的。自己电脑上安装的什么版本的JDK我们就设置对应的JDK版本就可以了。

大概在setting.xml中的第218行左右我们加入如下配置文件:

<profile>
    <id>jdk-1.8</id>
    <activation>
        <jdk>1.8</jdk>
        <activeByDefault>true</activeByDefault>
    </activation>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    </properties>
</profile>

我的是JDK1.8版本。

  • 版本不匹配会报错:java: 错误: 不支持发行版本 5

4.在IDEA中集成Maven

首先【File—>Settings—>Build,Execution,Deployment—>Build Tools—>Maven】。按照如下图操作:

然后我们再勾选一个地方。此处勾选的作用是,当修改pom文件时,Maven就会自动帮我们导包了。

这样IDEA中配置Maven就成功啦,然后下面我们用IDEA来创建Maven项目。

注意:上面这种方式设置的Maven是相对于当前项目而言的,如果再创建一个Maven项目可能又要配置Maven,所以我们要配置IDEA全局的Maven。(这个配置是可选,如果不嫌麻烦倒是可以设置一下)

①、【File—>Close Project】,我们找到界面右下角的Configure。然后【Project Defualt—>Settings】。

②、然后其它配置就是一样的了。

  • 在IDEA创建的Maven目录结构如下:

      hellomaven ——项目名称
        | –src ——源文件
        | ——-main ——存放主程序
        | —————java ——存放项目的.java文件
        | —————resources ——存放项目资源文件,如spring, hibernate配置文件
        | ——-test ——存放测试程序
        | ————-java ——存放所有单元测试.java文件,如JUnit测试类
        | ————-resources ——测试资源文件

        | —target ——项目输出位置,编译后的class文件会输出到此目录
        | —pom.xml ——maven项目核心配置文件

  • 我们使用maven项目来管理所依赖的jar包,就不需要手动向工程添加jar包,只需要在pom.xml文件(maven工程的配置文件Project Object Model)添加jar包的坐标即可,它就会自动从maven仓库中下载jar包、运行。

5.Maven的依赖管理

(1)Maven的坐标

我们知道,Maven的核心概念是依赖的管理,那么Maven是如果来引入jar包的呢?答:通过坐标。

在Maven中是,坐标是Jar包的唯一标识,然后Maven通过坐标在仓库中找到项目所需的Jar包。

如下代码中,groupId、artifactId和version构成了一个Jar包的坐标(简称gav)。

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

Maven坐标主要组成:

  • groupId:组织标识(包名)
  • artifactId:项目名称
  • version:项目的当前版本

(2)Maven的仓库

Maven的仓库,指的就是存放jar包的地方。 在Maven中的仓库分为三个类型:

  1. 本地仓库:就是用来存储从远程仓库或中央仓库下载的插件和jar包,项目使用一些插件或jar包,优先从本地仓库查找。默认本地仓库位置在 ${user.dir}/.m2/repository${user.dir}表示windows用户目录。修改地址可参考前面
  2. 远程仓库:就是当本地仓库没有相应的插件和jar包,maven会去远程仓库下载,默认回去中央仓库。远程仓库可以在互联网内也可以在局域网内。
  3. 中央仓库 :就是Maven团队自己维护的仓库,它服务于整个互联网,里面几乎存储了世界上大部分流行的开源项目构件,在maven软件中内置一个远程仓库地址。由于不在国内,所以我们从中央仓库下载jar包非常慢,所以国内推荐使用阿里巴巴维护的那个远程仓库。

image

Maven的各个仓库的优先级是:本地仓库 > 私服 > 远程仓库

(3)依赖的管理

​ 依赖指的是项目与jar包之间的相互依赖,比如我们在使用Spring框架的时候,需要用到Spring依赖的jar包。而依赖管理指的就是使用Maven来管理项目中使用到的jar包,Maven管理的方式就是“自动下载项目所需要的jar包,统一管理jar包之间的依赖关系”。比如在文件上传的时候,光光有common-fileupload这个jar包是不行的,common-fileupload还依赖于common-io这个包jar包,此时由于Maven的依赖管理机制,都会帮我们自动下载这些包,我们并不需要管。

image

我们一般添加依赖都会去Maven的仓库去查找,网站为:https://mvnrepository.com/

image

(4)依赖的配置

依赖的配置就是我们在Maven的项目中的pom.xml中来配置我们的依赖。这一步大家都知道,但是对于Maven坐标中的其它属性可能并不认识,下面介绍一下:

<dependencies>
    <dependency>
        <groupId>junit</groupId>     
        <artifactId>junit</artifactId>     
        <version>4.11</version>
        <package>...</package>
        <type>...</type>
        <scope>...</scope>
        <optional>...</optional>
        <exclusions>     
            <exclusion>     
              <groupId>...</groupId>     
              <artifactId>...</artifactId>     
            </exclusion>
      </exclusions>     
    </dependency>        
</dependencies>

这些元素标签的详细介绍:

  1. dependencies:用来管理依赖的总标签,一个 pom.xml 文件中只能存在一个这样的标签。
  2. dependency:包含在dependencies标签中,可以有无数个,每一个表示一个依赖
  3. groupId,artifactIdversion:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
  4. package:依赖的打包类型。普通项目为jar包,Web项目为war包。
  5. type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar。
  6. scope:依赖的范围,默认值是 compile。后面会进行详解。
  7. optional:标记依赖是否可选。
  8. exclusions:用来排除传递性依赖,后面会进行详细介绍。

(5)依赖的范围

依赖的范围:就是该依赖在各种环境下是否还可以使用。比如测试的jar包 junit4.11,,我们只希望在代码测试的时候使用,不希望在布署项目的时候把这个jar包放置上去,所以我们可以将Junit依赖设置为test范围的依赖。 还有server-api.jar, jsp-api.jar 是在代码编译的时候有用,但在布署项目的时候,是没有的用的,并且是不能把这个两个 jar包放置到tomcat里面的,因为tomcat里面有这两个jar包,会造成冲突, 所以一定要把这两个jar包给去掉,所以此时我们就要将它们设置为provided范围的依赖。这就是依赖的范围。

maven的依赖范围用 <scope>元素表示,其依赖范围有六种: compile、test、provided、runtime、import和system,其中import和system不常用,使用就不介绍了。

  1. complie:编译依赖,是默认的依赖范围。在maintest目录下的代码均可以访问这个范围下的依赖。布署到 tomcat下时,要把这个依赖放置在WEB-INF下的lib文件夹里面。
  2. test:测试依赖,仅仅是测试使用的。在main目录下的代码不能访问这个依赖,test目录下的代码可以访问这个依赖,布署到tomcat下时,不会把这个依赖放置在 WEB-INF下的lib文件夹里面。 如 junit 4.11这个依赖。
  3. provided:提供依赖。在maintest目录下的代码均可以访问这个依赖,但在布署到tomcat下时,不会把这个依赖放置在WEB-INF下的lib文件夹里面。 如 servlet-api, jsp-api
  4. runtime:运行依赖。main目录下的代码不能访问这个依赖, test目录下的代码可以访问这个依赖,布署到tomcat下时,会把这个依赖放置在WEB-INF下的lib文件夹里面。 如 jdbc 驱动

    总结成一张表就可以知道依赖范围表示的作用域如下:

依赖范围 对于编译执行环境有效 对于测试执行环境有效 对于运行时执行环境有效 例 子
compile spring-core
test junit
provided servlet-api
runtime jdbc

(6)依赖的传递

依赖的传递:如果我们的项目引用了一个Jar包,而该Jar包又引用了其它Jar包,那么此时的项目与引入的其它jar包就是传递依赖,但是在默认情况下项目编译时,Maven会把直接引用和间接引用的Jar包都下载到本地。

其中项目A直接依赖于B,而B也直接依赖于C,那么A就是间接依赖于C的,这就是传递性依赖。但是传递性依赖可能会产生依赖冲突。

如:项目A需要引入依赖包B,依赖包B又引入了依赖包C,那么B是A的直接依赖,C是A的传递依赖。如果项目A还需要引入依赖包D,依赖包D也引用了依赖包C,当依赖包B引用的C和依赖包D引用的C版本不同时,则发生了依赖冲突。

(7)依赖的冲突

刚刚在上面说了,如果项目中引入了多个相同的jar,而jar包的版本不同就会产生依赖冲突。但是Maven才不会这么蠢,Maven中采用了两种避免冲突的策略:①、短路径优先;②、声明优先。因此在Maven中实际上是不存在依赖冲突的。

①、短路优先:就是哪个传递依赖离它最近则就优先选择它。

通过上面的图片我们可以分析出:

  • 项目A——>B——>C——>D
  • 项目A——>B——>D

第二条路径是离项目A最近的,也就是路径最短的。所以项目A最后用的是B依赖的D的jar包

但是我们思考一下,如果传递依赖的路径相同那么它们会怎么选择呢?我们接着往下看。

②、声明优先:就是谁先声明的就先选择谁呗。

通过上面的图片我们可以分析出:

  • 项目A——>B——>C
  • 项目A——>D——>C

可以发现它们的路径是一样的,那此时就是按照谁先声明的就选择谁。在pom.xml的依赖 <dependencies > </dependencies > 里面, 先放置哪一个,就用哪一个。你先放置B, 那么C就是 B里面的,你先放置D,那么C就是D里面的。 所以在放置依赖时,有一定的顺序

(8)依赖的排除

依赖的排除:就是排除掉我们不想要的依赖。比如:项目A依赖于B,B依赖于C,但有可能C依赖是不兼容的,会对项目造成一定的影响。此时,我们可以使用关键字exclusion排除C依赖。我们以spring-aop依赖为例:spring-aop是依赖于spring-beans和spring-core的。

假如我们需要排除掉spring-beans:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.2.6.RELEASE</version>
  <exclusions>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
    </exclusion>
  </exclusions>
</dependency>

排除后的结果为;

(9)版本的管理

我们对版本的管理暂且有两种方式:①、版本锁定;②、版本常量。

①、版本锁定

版本锁定:指的是锁定项目中依赖的版本。这种是目前实际项目中使用的最多的。版本锁定需要使用到dependencyManagement元素。需要说明的是dependencyManagement仅仅起到指锁定依赖版本的作用,其它的作用它什么都没有。而真正依赖包的下载任然要定义在dependencie元素中。

<!--锁定依赖的版本,它仅仅是起到锁定作用,不下载依赖包-->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
 
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>
 
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>
 
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>
  </dependencies>
</dependencyManagement>
 
<!--依赖包的下载仍然有dependencies管理-->
<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <!--<version>4.11</version>-->
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <!--<version>4.1.6.RELEASE</version>-->
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.6.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <!--<version>4.1.6.RELEASE</version>-->
  </dependency>
</dependencies>

当我们使用dependencyManagement元素来锁定依赖版本后,dependencie元素中的依赖版本可写,也可不写,但是依赖版本引入则就有两种不同的方式了:

  1. 在dependencies中的依赖中如果没有声明依赖的版本,就到dependenciesManage中去找,找到就使用锁定的版本号,没有就报错。
  2. 在dependencies中声明了依赖的版本,则使用该依赖的版本,不管在dependenciesManage中有没有声明依赖的版本,都以dependencies中声明的版本为主。

dependencies和dependencyManagement的区别:

  • dependencies是真正引入依赖的元素。而且在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)。
  • dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的在dependencies中声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的依赖版本。

②、版本常量

首先我们在pom.xml文件中提取出各自依赖的版本常量。封装至properties元素中,然后在依赖的version元素中用OGNL表达式获取即可,版本常量方式用的也很多。

这种方式方便了项目版本依赖管理的统一和后面的升级,而且它还具有继承性,在父项目pom.xml文件中定义的版本常量,在子模块中的pom.xml文件也能使用。

<!--定义版本常量-->
<properties>
  <spring.version>5.2.6.RELEASE</spring.version>
  <junit.version>4.11</junit.version>
</properties>
 
<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <!--使用版本常量-->
    <version>${junit.version}</version>
    <scope>test</scope>
  </dependency>
 
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
  </dependency>
</dependencies>

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!