Maven的基础知识
文章目录
- Maven的基础知识
- maven中文网
- 初识maven
- 约定大于配置的概念
- maven的约定配置
- 标准的maven目录结构
- Maven 重要的几个特点
- maven的pom
- maven的构建生命周期
- 构建阶段由插件目标构成
- Maven 有以下三个标准的生命周期
- maven常见的命令
- 使用java命令运行jar包
- maven插件配置
- maven名词
- 使用maven命令创建项目
- 编译和测试
- maven的快照
- Maven从仓库中解析依赖的机制
- maven配置镜像仓库
- 镜像和私服的结合
- maven依赖配置和依赖范围
- 依赖范围
- import
- 传递性依赖
- classpath类路径的理解
- dependencyManagement使用简介
- 常见问题
- maven项目pom.xml中parent标签的使用
- pom打包
- pom打包
maven中文网
http://c.biancheng.net/view/5295.html
初识maven
Maven 翻译为"专家"、“内行”,是 Apache 下的一个纯 Java 开发的开源项目。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。
Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。
Maven 也可被用于构建和管理各种项目,例如 C#,Ruby,Scala 和其他语言编写的项目。Maven 曾是 Jakarta 项目的子项目,现为由 Apache 软件基金会主持的独立 Apache 项目。
约定大于配置的概念
开发人员仅需规定应用中不符合约定的部分在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想总的来说,上面两条都遵循了推荐默认配置的思想。当存在特殊需求的时候,自定义配置即可。这样可以大大的减少配置工作,这就是所谓的“约定”。
maven的约定配置
Maven 提倡使用一个共同的标准目录结构,Maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构。如下所示:
标准的maven目录结构
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的构建生命周期
为了完成 default 生命周期,这些阶段(包括其他未在上面罗列的生命周期阶段)将被按顺序地执行。
构建阶段由插件目标构成
Maven 有以下三个标准的生命周期
- clean:项目清理的处理
- default(或 build):项目部署的处理
- site:项目站点文档创建的处理
clean生命周期
如果我们运行 mvn post-clean ,则运行以下三个生命周期阶段:
pre-clean, clean, post-clean
Default (Build) 生命周期
这是 Maven 的主要生命周期,被用于构建应用,包括下面的 23 个阶段:
site生命周期
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插件配置
插件的可复制性
始终定义构建使用的插件的每个版本,以保证构建的可复制性。一个好的做法是在每个构建插件的元素中指定它们。(通常,您将在父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 窗口,操作步骤如下所示:
- 将目录切换到工程目录下(HelloWorld)。
- 输入“mvn clean”,按 Enter 键清空以前编译安装过的历史结果。
- 输入“mvn compile”,按 Enter 键编译源代码。
- 输入“mvn test”,按 Enter 键运行测试案例进行测试。
- 输入“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,将那些必要的间接依赖以传递依赖的形式引入项目中。
当然,传递依赖在将间接依赖引入项目的过程中也有它自己的规则和范围。这个规则和范围是同前面介绍的依赖范围紧密关联的。
通过前面的表格,可以得出如下规律。
- 当第二直接依赖为 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
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
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项目才能使用它。