Maven
一、Maven中的几个概念
1. POM
我们在上篇文章中提到了maven的几个核心概念,其中一个是POM。
POM就是Project Object Model项目对象模型。
将 Java 工程的相关信息封装为对象作为便于操作和管理的模型。
pom.xml就是Maven 工程的核心配置。可以说学习 Maven 就是学习 pom.xml 文件中的配置。
我们会在后面不断的学习,这里就先不做展开。
2. 座标
说到座标,我们首先想到的应该是数学中的座标:
- 在一个平面中使用 x、y 两个向量可以唯一的确定平面中的一个点。
- 在空间中使用 x、y、z 三个向量可以唯一的确定空间中的一个点。
而maven中的座标,
使用以下三个向量在Maven仓库中唯一的确定一个Maven工程。
groupID
:公司或组织的域名倒序+当前项目的名称artifactID
:当前项目的模块名称version
:当前模块的版本
例如:
<groupId>com.atguigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
这样的定位方式我们又简称为GAV,也就是上面三个单词的首字母缩写。
Maven工程的座标与仓库中路径的对应关系,
例如:
3. 仓库
分类
仓库分为本地仓库和远程仓库。
本地仓库就是当前电脑上部署的仓库目录,为当前电脑上所有的Maven工程服务。
远程仓库又分为私服、中央仓库、中央仓库的镜像。
- 私服:架设在当前局域网环境下,为当前局域网范围内的所有 Maven 工程服务。
- 中央仓库:架设在 Internet 上,为全世界所有 Maven 工程服务。
- 中央仓库的镜像:架设在各个大洲,为中央仓库分担流量。减轻中央仓库的压力,同时更快的响应用户请求。
仓库中的内容
仓库里面保存的都是Maven工程。
仓库中保存着:
- Maven自身所需要的插件
- 第三方框架或工具的jar包
- 我们自己开发的Maven工程
我们为了介绍Maven的依赖,我们先要建立第二个Maven工程。
二、第2个Maven工程
搭建HelloFriend工程
①工程名:HelloFriend
②目录结构与第一个Maven工程相同
③POM文件
<?xml version="1.0" ?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.veeja.maven</groupId>
<artifactId>HelloFriend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>HelloFriend</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.veeja.maven</groupId>
<artifactId>HelloMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
④主程序:
在src/main/java/com/veeja/maven目录下新建文件HelloFriend.java
package com.veeja.maven;
import com.veeja.maven.HelloMaven;
public class HelloFriend {
public String sayHelloToFriend(String name){
HelloMaven helloMaven = new HelloMaven();
String str = helloMaven.sayHello(name)+" I am "+this.getMyName();
System.out.println(str);
return str;
}
public String getMyName(){
return "John";
}
}
⑤测试程序:
在/src/test/java/com/atguigu/maven目录下新建测试文件HelloFriendTest.java
package com.veeja.maven;
import static junit.framework.Assert.assertEquals;
import org.junit.Test;
import com.veeja.maven.HelloMaven;
public class HelloFriendTest {
@Test
public void testHelloFriend(){
HelloFriend helloFriend = new HelloFriend();
String results = helloFriend.sayHelloToFriend("veeja");
assertEquals("Hello veeja! I am John",results);
}
}
我们的关注点在于,我们在这个HelloFriend工程里面,用到了HelloMaven这个工程。这就产生了所谓的“依赖”。而我们为什么能使用呢,原因就在于我们在pom.xml中配置了这样的依赖关系,也就是:
<dependency>
<groupId>com.veeja.maven</groupId>
<artifactId>HelloMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
那么我们这样配置到底能不能使用呢?我们接下来要试一试。
执行几个命令
我们先来执行mvn compile
命令,看一下结果:
我们可以看到,构建失败了,错误的信息为:
[ERROR] Failed to execute goal on project HelloFriend: Could not resolve dependencies for project com.veeja.maven:HelloFriend:jar:0.0.1-SNAPSHOT: Could not find artifact com.veeja.maven:HelloMaven:jar:0.0.1-SNAPSHOT -> [Help 1]
它提示我们,找不到HelloMaven工件。
我们再来思考上面讲的座标知识,
<dependency>
<groupId>com.veeja.maven</groupId>
<artifactId>HelloMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
这样的一段依赖配置,已经确定了对应的目录。而我们在仓库中,并没有找到相应的目录及文件。
三、依赖
Maven解析依赖信息时会到本地仓库中查找被依赖的jar包。
对于我们自己开发的Maven工程,使用install命令
安装后就可以进入仓库。
对于上面的例子中,我们应该先把HelloMaven添加进仓库中,也就是在HelloMaven工程下执行mvn install
。
这时呢,我们在进入我们的本地仓库中,就能找到对应的目录和文件了。
这时我们再次编译HelloFriend工程,
我们这次看到,编译就成功了。
依赖的范围
我们在配置文件中的<dependency>
标签中,
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.veeja.maven</groupId>
<artifactId>HelloMaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
发现有一个<scope>
的子标签,这就是指的依赖的范围。
依赖的范围常用的三个取值为compile
、test
、provided
三个。
① 从项目结构的角度理解compile和test的区别
结合具体例子:
对于HelloFriend来说,HelloMaven就是服务于主程序的,junit是服务于测试程序的。
HelloFriend主程序需要 HelloMaven是非常明显的,测试程序由于要调用主程序所以也需要HelloMaven,所以compile范围依赖对主程序和测试程序都应该有效。
HelloFriend的测试程序部分需要junit也是非常明显的,而主程序是不需要的,所以test范围依赖仅仅对于主程序有效。
② 从开发和运行这两个不同阶段理解 compile 和 provided 的区别
③ 有效性总结
[1]compile范围依赖
- 对主程序是否有效:有效
- 对测试程序是否有效:有效
- 是否参与打包:参与
- 是否参与部署:参与
- 典型例子: spring-core
[2]test范围依赖
- 对主程序是否有效:无效
- 对测试程序是否有效:有效
- 是否参与打包:不参与
- 是否参与部署:不参与
- 典型例子: junit
[3]provided范围依赖
- 对主程序是否有效:有效
- 对测试程序是否有效:有效
- 是否参与打包:不参与
- 是否参与部署:不参与
- 典型例子: servlet-apijar
关于依赖的其他几个特点,我们将在后面进行介绍。
四、Maven的生命周期
1. 什么是生命周期?
各个构建环节的执行顺序不能打乱,必须按照既定的正确的顺序来执行。
Maven的核心程序中定义了抽象的生命周期,生命周期中各个阶段的具体任务是由插件来完成的。
Maven 生命周期定义了各个构建环节的执行顺序,有了这个清单,Maven 就可以自动化的执行构建命 令了。
Maven 有三套相互独立的生命周期,分别是:
- Clean Lifecycle 在进行真正的构建之前进行一些清理工作。
- Default Lifecycle 构建的核心部分,编译,测试,打包,安装,部署等等。
- Site Lifecycle 生成项目报告,站点,发布站点。
它们是相互独立的,你可以仅仅调用 clean 来清理工作目录,仅仅调用 site 来生成站点。当然你也可以 直接运行 mvn clean install site 运行所有这三套生命周期。
每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应于一个特定的阶段。比 如,运行 mvn clean,这个 clean 是 Clean 生命周期的一个阶段。有 Clean 生命周期,也有 clean 阶段。
2. Clean生命周期
Clean 生命周期一共包含了三个阶段:
①pre-clean 执行一些需要在 clean 之前完成的工作
②clean 移除所有上一次构建生成的文件
③post-clean 执行一些需要在 clean 之后立刻完成的工作
3. Site生命周期
①pre-site 执行一些需要在生成站点文档之前完成的工作
②site 生成项目的站点文档
③post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
④site-deploy 将生成的站点文档部署到特定的服务器上
这里经常用到的是 site 阶段和 site-deploy 阶段,用以生成和发布 Maven 站点,这可是 Maven 相当强大 的功能,Manager 比较喜欢,文档及统计数据自动生成,很好看。
4. Default生命周期
Default 生命周期是 Maven 生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里, 只解释一些比较重要和常用的阶段:
validate
generate-sources
process-sources
generate-resources
process-resources
复制并处理资源文件,至目标目录,准备打包。
compile
编译项目的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
复制并处理资源文件,至目标测试目录。
test-compile
编译测试源代码。
process-test-classes test
使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package package
接受编译好的代码,打包成可发布的格式,如 JAR。
pre-integration-test
integration-test
post-integration-test
verify install
将包安装至本地仓库,以让其它项目依赖。
deploy
将最终的包复制到远程的仓库,以让其它开发人员与项目共享或部署到服务器上运行。
5. 生命周期与自动化构建
运行任何一个阶段的时候,它前面的所有阶段都会被运行,例如我们运行 mvn install 的时候,代码会 被编译,测试,打包。这就是 Maven 为什么能够自动执行构建过程的各个环节的原因。此外,Maven 的插 件机制是完全依赖 Maven 的生命周期的,因此理解生命周期至关重要。
五、插件和目标
Maven 的核心仅仅定义了抽象的生命周期,具体的任务都是交由插件完成的。
每个插件都能实现多个功能,每个功能就是一个插件目标。
Maven 的生命周期与插件目标相互绑定,以完成某个具体的构建任务。
例如:compile
就是插件 maven-compiler-plugin 的一个目标;pre-clean
是插件 maven-clean-plugin 的一个目标。
END.