Maven的基础知识

本文深入介绍了Maven的基础知识,包括约定优于配置的理念、标准目录结构、重要特性和生命周期。讲解了pom.xml的核心概念,如父pom、构建生命周期和插件配置,以及常用命令。此外,还涵盖了依赖管理、快照、仓库解析机制和配置镜像仓库。文章旨在帮助读者全面理解并掌握Maven的使用。
摘要由CSDN通过智能技术生成

Maven的基础知识

maven中文网

http://c.biancheng.net/view/5295.html

初识maven

Maven 翻译为"专家"、“内行”,是 Apache 下的一个纯 Java 开发的开源项目。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。

Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。

Maven 也可被用于构建和管理各种项目,例如 C#,Ruby,Scala 和其他语言编写的项目。Maven 曾是 Jakarta 项目的子项目,现为由 Apache 软件基金会主持的独立 Apache 项目。

约定大于配置的概念

开发人员仅需规定应用中不符合约定的部分在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想总的来说,上面两条都遵循了推荐默认配置的思想。当存在特殊需求的时候,自定义配置即可。这样可以大大的减少配置工作,这就是所谓的“约定”。

image-20211001181640605

maven的约定配置

Maven 提倡使用一个共同的标准目录结构,Maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构。如下所示:

image-20211001181828854

image-20211001181842791

标准的maven目录结构

image-20211002133607683

Maven 重要的几个特点

  • 任意工程中共享。

  • 依赖管理包括自动更新。

  • 可扩展,能够轻松编写 Java 或脚本语言的插件

  • 向后兼容性 − 您可以很轻松的从旧版本 Maven 的多个模块移植到 Maven 3 中。

  • 子项目使用父项目依赖时,正常情况子项目应该继承父项目依赖,无需使用版本号,

  • 并行构建 − 编译的速度能普遍提高20 - 50 %。

  • 更好的错误报告 − Maven 改进了错误报告,它为您提供了 Maven wiki 页面的链接,您可以点击链接查看错误的完整描述。

  • 基于模型的构建 − Maven能够将任意数量的项目构建到预定义的输出类型中,如 JAR,WAR 或基于项目元数据的分发,而不需要在大多数情况下执行任何脚本。

maven的pom

父pom

父(Super)POM是 Maven 默认的 POM。所有的 POM 都继承自一个父 POM(无论是否显式定义了这个父 POM)。父 POM 包含了一些可以被继承的默认设置。因此,当 Maven 发现需要下载 POM 中的 依赖时,它会到 Super POM 中配置的默认仓库 http://repo1.maven.org/maven2 去下载。

maven的构建生命周期

image-20211001182656670

image-20211001182707826

为了完成 default 生命周期,这些阶段(包括其他未在上面罗列的生命周期阶段)将被按顺序地执行。

构建阶段由插件目标构成

image-20211001183026005

Maven 有以下三个标准的生命周期

  • clean:项目清理的处理
  • default(或 build):项目部署的处理
  • site:项目站点文档创建的处理

clean生命周期

image-20211001183151467

如果我们运行 mvn post-clean ,则运行以下三个生命周期阶段:

pre-clean, clean, post-clean

Default (Build) 生命周期

这是 Maven 的主要生命周期,被用于构建应用,包括下面的 23 个阶段:

image-20211001183616047

image-20211001183636101

image-20211001183644731

image-20211001183759127

site生命周期

image-20211001183836776

image-20211001184031966

maven常见的命令

1. 创建Maven的普通Java项目:

mvn archetype:create
    -DgroupId=packageName
    -DartifactId=projectName

2. 创建Maven的Web项目:

mvn archetype:create
    -DgroupId=packageName
    -DartifactId=webappName
    -DarchetypeArtifactId=maven-archetype-webapp

3. 反向生成 maven 项目的骨架:

mvn archetype:generate

你是怎么创建你的maven项目的?是不是像这样:

mvn archetype:create -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.ryanote -Dartifact=common

如果你还再用的话,那你就out了,现代人都用mvn archetype:generate了,它将创建项目这件枯燥的事更加人性化,你再也不需要记那么多的archetypeArtifactId,你只需输入archetype:generate,剩下的就是做”选择题”了.

4. 编译源代码:

mvn compile

5. 编译测试代码:

mvn test-compile

6. 运行测试:

mvn test

清除产生的项目:

mvn clean

只打jar包:

mvn jar:jar

生成target目录,编译、测试代码,生成测试报告,生成jar/war文件 :

mvn package

使用java命令运行jar包

然后我们可以在控制台里输入java -jar test.jar

即可以运行这个jar。

maven插件配置

image-20211002134247745

插件的可复制性

始终定义构建使用的插件的每个版本,以保证构建的可复制性。一个好的做法是在每个构建插件的元素中指定它们。(通常,您将在父POM中定义一个元素。)对于报告插件,请在元素中指定每个版本(当然也在元素中)。

MOJO配置

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-myquery-plugin</artifactId>
        <version>1.0</version>
        <configuration>
          <url>http://www.foobar.com/query</url>
          <timeout>10</timeout>
          <options>
            <option>one</option>
            <option>two</option>
            <option>three</option>
          </options>
        </configuration>
      </plugin>
    </plugins>
  </build>
  ...
</project>

maven名词

groupId 和 artifactId :

这里的 groupId 和 artifactId 同部门名称和组名称一样,用来唯一确定一个项目(软件、功能)。有些地方会把这两个描述的信息合起来叫“坐标”。

使用maven命令创建项目

1)在硬盘上创建一个空的目录,用来存放 Maven 项目,如 E:\temp\demoMaven。

2)打开 CMD 窗口,用 cd 命令,切换到 demoMaven 目录,如图 1 所示。

3)在 CMD 窗口中输入如下命令并按 Enter 键。

mvn org.apache.maven.plugins:maven-archetype-plugin:2.2:creat
-DgroupId=com.mengma.demo
-DartifactId=HelloWorld
-DpackageName=com.mengma.demo

注意上述:注:

  • org.apache.maven.plugins:maven-archetype-plugin:2.2,指使用 groupId 为 org.apache.maven.plugins,artifactId 为 maven-archetype-plugin,版本为 2.2 的 Archetype插件。
  • -DgroupId=cn.com.mvnbook.demo,指定要创建的工程的 groupId。
  • -DartifactId=MVNBookTP01,指定工程的 artifactId。
  • -DpackageName=cn.com.mvnbook.demo.tp01,指定工程代码的标准包

编译和测试

打开 CMD 窗口,操作步骤如下所示:

  1. 将目录切换到工程目录下(HelloWorld)。
  2. 输入“mvn clean”,按 Enter 键清空以前编译安装过的历史结果。
  3. 输入“mvn compile”,按 Enter 键编译源代码。
  4. 输入“mvn test”,按 Enter 键运行测试案例进行测试。
  5. 输入“mvn install”,按 Enter 键,将当前代码打成 jar 包,安装到 Maven 的本地管理目录下,其他 Maven 工程只要指定坐标就可以使用。

maven的快照

快照意思:

快照是数据存储的某一时刻的状态记录

自我理解:maven的快照就是将需要的依赖发布到一个私服中(当然这个依赖并不完整,一直处于更新中。会记录它每个版本的发布时间。下次如果谁要使用这个依赖的话,就会通过maven自动下载最新的依赖下来–因为有时间戳记录的嘛。)

快照的潜在风险

项目不应该依赖任何团队外部的快照版本依赖。由于快照版本的不稳定性,这样的依赖会造成潜在的危险。也就是说,即使项目构建这次成功了,由于外部的快照版本依赖会随时间改变而再次更新,下次构建的时候有可能会失败。

Maven从仓库中解析依赖的机制

Maven 在寻找项目需要的依赖的顺序是:先在本地仓库中查找,如果没有找到,再找远程仓库,找到后下载;如果依赖的版本为快照版本,Maven 除了找到对应的构件外,还会自动查找最新的快照。这个找依赖的过程如下所示。

1)当依赖的范围是 system 的时候,Maven 直接从本地文件系统中解析构件。

2)根据依赖坐标计算仓库路径,尝试直接从本地仓库寻找构件,如果发现对应的构件,就解析成功。

3)如果在本地仓库不存在相应的构件,就遍历所有的远程仓库,发现后,下载并解析使用。

4)如果依赖的版本是 RELEASE 或 LATEST,就基于更新策略读取所有远程仓库的元数据文件(groupId/artifactId/maven-metadata.xml),将其与本地仓库的对应元合并后,计算出 RELEASE 或者 LATEST 真实的值,然后基于该值检查本地仓库,或者从远程仓库下载。

5)如果依赖的版本是 SNAPSHOT,就基于更新策略读取所有远程仓库的元数据文件,将它与本地仓库对应的元数据合并,得到最新快照版本的值,然后根据该值检查本地仓库,或从远程仓库下载。

6)如果最后解析得到的构件版本包含有时间戳,先将该文件下载下来,再将文件名中时间戳信息删除,剩下 SNAPSHOT 并使用(以非时间戳的形式使用)。

maven配置镜像仓库

如果仓库 A 能提供仓库 B 存储的所有服务,那么就把 A 叫作 B 的镜像。

由于地理位置的因素,该镜像往往能够提供比中央仓库更快的服务

比如 http://maven.net.cn/content/groups/public 就是中央仓库 http://repo1.maven.org/maven2/ 在中国的镜像。

如下配置:

<settings>
    ...
    <mirrors>
        <mirror>
            <id>maven.net.cn</id>
            <name>中央仓库在中国的镜像</name>
            <url>http://maven.net.cn/content/groups/public/</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
        ...
    </mirrors>
    ...
</settings>

镜像和私服的结合

其实在实际工作中,关于镜像有一个最常见的用法,那就是结合私服使用。由于私服是用来代替所有的外部公共仓库的,包括中央仓库,所以对于团队内部的 Maven 用户来说,使用一个私服地址就等于使用了所有的外部仓库。

这样就可以将对外部远程仓库的访问配置都集成到私服上来,从而简化 Maven 本身的配置。为达到这样的目标,可以配置一个如下内容的镜像

<settings>
    ...
    <mirrors>
        <mirror>
            <id>internal-repository</id>
            <name>Internal Repository Manager</name>
            <url>http://192.168.1.207:8080/repository/internal</url>
            <mirrorOf>*</mirrorOf>
        </mirror>
        ...
    </mirrors>
    ...
</settings>

上面配置信息中,mirrorOf 的值为 *,表示是所有 Maven 仓库的镜像。任何对远程仓库的请求都会转向到 207 这台计算机的私服上去。如果私服需要认证,统一配置一个 id 为 internal-repository 的 server 就可以了。

当然,关于 mirrorOf 还有一些特别的配置方式。

  • *:匹配所有的远程仓库。
  • external:*:匹配所有的远程仓库,使用 localhost、file:// 协议的除外。也就是说,匹配所有非本地的远程仓库。
  • r1,r2:匹配指定的几个远程仓库,每个仓库之间用逗号隔开。
  • *,! r1,r2:匹配除了指定仓库外的所有仓库,“!”后面的仓库是被排除外的。

maven依赖配置和依赖范围

  • groupId、artifactId 和 version:依赖的基本坐标。对于任何依赖,基本坐标是最基本、最重要的,因为 Maven 是根据坐标找依赖的。
  • type:依赖的类型,同项目中的 packaging 对应。大部分情况不需要声明,默认是 jar。
  • scope:依赖的范围,详细情况后面介绍。
  • optional:标记依赖是否可选,详细情况后面介绍。
  • exclusions:排除传递性依赖,详细情况后面介绍。

依赖范围

Java 中有个环境变量叫 classpath。JVM 运行代码的时候,需要基于 classpath 查找需要的类文件,才能加载到内存执行。

Maven 在编译项目主代码的时候,使用的是一套 classpath,主代码编译时需要的依赖就添加到这个 classpath 中去;Maven 在编译和执行测试代码的时候,又会使用一套 classpath,这个动作需要的依赖就添加到这个 classpath 中去;Maven 项目具体运行的时候,又有一个独立的 classpath,同样运行时需要的依赖,肯定也要加到这个 classpath 中。这些 classpath,就是依赖的范围。

依赖的范围,就是用来控制这三种 classpath 的关系(编译 classpath、测试 classpath 和运行 classpath),接下来分别介绍依赖的范围的名称和意义。

1)compile

编译依赖范围。如果在配置的时候没有指定,就默认使用这个范围。使用该范围的依赖,对编译、测试、运行三种 classpath 都有效。

2)test

测试依赖范围。使用该范围的依赖只对测试 classpath 有效,在编译主代码或运行项目的时候,这种依赖是无效的。

3)provided

已提供依赖范围。使用此范围的依赖,只在编译和测试 classpath 的时候有效,运行项目的时候是无效的。比如 Web 应用中的 servlet-api,编译和测试的时候就需要该依赖,运行的时候,因为容器中自带了 servlet-api,就没必要使用了。如果使用了,反而有可能出现版本不一致的冲突。

4)runtime

运行时依赖范围。使用该范围的依赖,只对测试和运行的 classpath 有效,但在编译主代码时是无效的。比如 JDBC 驱动实现类,就需要在运行测试和运行主代码时候使用,编译的时候,只需 JDBC 接口就行。

5)system

系统依赖范围。该范围与 classpath 的关系,同 provided 一样。但是,使用 system 访问时,必须通过 systemPath 元素指定依赖文件的路径。因为该依赖不是通过 Maven 仓库解析的,建议谨慎使用

import

导入依赖范围。该依赖范围不会对三种 classpath 产生实际的影响。它的作用是将其他模块定义好的 dependencyManagement 导入当前 Maven 项目 pom 的 dependencyManagement 中。比如有个 SpringPOM Maven 工程,它的 pom 中的 dependencyManagement 配置如下:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>cn.com.mvn.pom</groupId>
            <artifactId>SpringPOM</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

它会将项目SpringPOM中的 pom 中定义的 dependencyManagement 原样合并过来。
依赖全部导入到该项目的pom.xml文件中。

传递性依赖

Maven 的传递依赖机制就能解决这样的问题。

当项目基于 Spring 框架实现的时候,只需将 Spring 的依赖配置到 pom 的依赖元素就行。至于 Spring 框架所依赖的第三方 jar 包,用户不用处理,Maven 自己通过检测 Spring 框架的依赖信息将它们导入项目中来。而且只会导入 Spring 框架所需要的,不会导入多余的依赖。

也就是说,Maven 会解析项目中的每个直接依赖的 pom,将那些必要的间接依赖以传递依赖的形式引入项目中。

当然,传递依赖在将间接依赖引入项目的过程中也有它自己的规则和范围。这个规则和范围是同前面介绍的依赖范围紧密关联的。

image-20211002145831663

通过前面的表格,可以得出如下规律。

  • 当第二直接依赖为 compile 的时候,传递依赖同第一直接依赖一致。
  • 当第二直接依赖为 test 的时候,没有传递依赖。
  • 当第二直接依赖为 provided 的时候,值将第一直接依赖中的 provided 以 provided 的形式传递。
  • 当第二直接依赖为 runtime 的时候,传递依赖的范围基本上同第一直接依赖的范围一样,但 compile 除外,compile 的传递依赖范围为 runtime。

classpath类路径的理解

日常的开发中或者初学者中,都是直接使用工具进行Java的开发或学习,如eclipse,idea、myeclipse这类的开发工具,由于大部分操作都是由开发工具所完成,所以开发中并不关注classpath这个属性,日久之后就忘了这个属性到底是做什么的。

classpath只得是类加载时的路径,当我们通过java 类名称来执行一个java类时,此时就启动了Java虚拟机来解释所需要执行的*.class文件,Java虚拟机是通过classpath属性配置的路径来找到所需要解释的*.class文件的,默认情况下,classpath都表示当前目录,比如当前目录在D盘,那么classpath就是表示此时在D盘,不会在C盘,E盘。

使用dos命令运行java代码

在目录:D:\java示例代码创建一个A.java文件。里面代码如下:

public class A{
	public static void main(String[] args){
		System.out.println("hello world!!");
}
}
//  注意:文件名要和类名相同

接着在dos命令进入该目录

编译:javac A.java

运行(让解释器解释):java A.java

image-20211002143714388

dependencyManagement使用简介

Maven中的dependencyManagement元素提供了一种管理依赖版本号的方式。在dependencyManagement元素中声明所依赖的jar包的版本号等信息,那么所有子项目再次引入此依赖jar包时则无需显式的列出版本号。Maven会沿着父子层级向上寻找拥有dependencyManagement 元素的项目,然后使用它指定的版本号。

<!--父项目-->
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>1.2.3.RELEASE</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

此配置即生命了spring-boot的版本信息。

子项目则无需指定版本信息:

<!-- 子项目 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

使用优点
如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号。当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要逐个修改子项目;另外如果某个子项目需要另外的一个版本,只需要声明version即可。

注意事项
dependencyManagement中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用的依赖。

对pom文件中${}的理解

参考网址:https://blog.csdn.net/cai_ing/article/details/109226222

自定义属性:在pom中元素下自定义的Maven属性。例如

<project>  
    <properties>  
        <my.prop>hello</my.prop>  
    </properties>  
</project>

Settings属性:与POM属性同理。如${settings.localRepository}指向用户本地仓库的地址。

Java系统属性:所有Java系统属性都可以使用Maven属性引用,例如${user.home}指向了用户目录。可以通过命令行mvn help:system查看所有的Java系统属性

环境变量属性:所有环境变量都可以使用以env.开头的Maven属性引用。例如${env.JAVA_HOME}指代了JAVA_HOME环境变量的值。也可以通过命令行mvn help:system查看所有环境变量。

常见问题

1.Could not transfer artifact org.springframework.xxx:spring-xxx-dependencies

image-20211003131523984

maven项目pom.xml中parent标签的使用

现在有这样一个场景,有两个web项目A、B,一个java项目C,它们都需要用到同一个jar包:common.jar。如果分别在三个项目的pom文件中定义各自对common.jar的依赖,那么当common.jar的版本发生变化时,三个项目的pom文件都要改,项目越多要改的地方就越多,很麻烦。这时候就需要用到parent标签, 我们创建一个parent项目,打包类型为pom,parent项目中不存放任何代码,只是管理多个项目之间公共的依赖。在parent项目的pom文件中定义对common.jar的依赖,ABC三个子项目中只需要定义,parent标签中写上parent项目的pom坐标就可以引用到common.jar了。

简单来说就是:使用parent可以让依赖变得简单,多个项目依赖同一个父项目的依赖,当依赖的version发生变化时,只要更改父项目的依赖就可以了。不然每个子项目都要单独进行更改。

上面的问题解决了,我们在切换一个场景,有一个springmvc.jar,只有AB两个web项目需要,C项目是java项目不需要,那么又要怎么去依赖。如果AB中分别定义对springmvc.jar的依赖,当springmvc.jar版本变化时修改起来又会很麻烦。解决办法是在parent项目的pom文件中使用将springmvc.jar管理起来,如果有哪个子项目要用,那么子项目在自己的pom文件中使用

果有哪个子项目要用,那么子项目在自己的pom文件中使用

<dependency>
  <groupId></groupId>
   <artifactId></artifactId>
</dependency>

标签中写上springmvc.jar的坐标,不需要写版本号,可以依赖到这个jar包了。这样springmvc.jar的版本发生变化时只需要修改parent中的版本就可以了。

pom打包

maven install 会将项目输出的jar安装到了maven本地仓库中,可以打开相应的文件夹看到项目的pom和jar,之后其他maven项目才能使用它。

换一个场景,有一个springmvc.jar,只有AB两个web项目需要,C项目是java项目不需要,那么又要怎么去依赖。如果AB中分别定义对springmvc.jar的依赖,当springmvc.jar版本变化时修改起来又会很麻烦。解决办法是在parent项目的pom文件中使用将springmvc.jar管理起来,如果有哪个子项目要用,那么子项目在自己的pom文件中使用

果有哪个子项目要用,那么子项目在自己的pom文件中使用

<dependency>
  <groupId></groupId>
   <artifactId></artifactId>
</dependency>

标签中写上springmvc.jar的坐标,不需要写版本号,可以依赖到这个jar包了。这样springmvc.jar的版本发生变化时只需要修改parent中的版本就可以了。

pom打包

maven install 会将项目输出的jar安装到了maven本地仓库中,可以打开相应的文件夹看到项目的pom和jar,之后其他maven项目才能使用它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值